From bd5d7f6294166c4262c6d57acf8d1fa1468048c1 Mon Sep 17 00:00:00 2001 From: Keigo Healy Date: Sat, 27 Sep 2025 16:48:00 -0500 Subject: [PATCH 1/8] main ui created --- src/app/eme/page.tsx | 75 ++++++++++++++++++++++++++++++++++++++++++++ src/app/globals.css | 2 +- src/constants/eme.ts | 20 ++++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/app/eme/page.tsx create mode 100644 src/constants/eme.ts diff --git a/src/app/eme/page.tsx b/src/app/eme/page.tsx new file mode 100644 index 0000000..0a69305 --- /dev/null +++ b/src/app/eme/page.tsx @@ -0,0 +1,75 @@ +"use client" +import { useState, useEffect } from "react"; +import { Message, botDummy, userDummy } from "@/constants/eme"; + +export default function emePage() { + const [messages, setMessages] = useState([userDummy, botDummy, userDummy, botDummy, userDummy, botDummy]) + const [prompt, setPrompt] = useState("") + const [isLoading, setIsLoading] = useState(false) + + useEffect(() => { + + }, []) + + const submitPrompt = async (e: React.FormEvent) => { + if (isLoading || prompt == "") + return; + + } + + return ( +
+

+ eme +

+

+ eme is the Emerging Coders chatbot that answers your questions about navigating CS at Northwestern. Answers are based on EMCO GroupMe messages. +

+ +
+ {messages.map((msg, i) => ( +
+
+
+ {msg.text} +
+
+
+ ))} +
+ +
+
+ setPrompt(e.target.value)} + className="w-full bg-black border-zinc-800 text-white focus-visible:ring-purple-500/30 px-5 py-2" + /> + +
+
+
+ ) +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index e099472..55f3755 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -46,7 +46,7 @@ } :root { - --background: oklch(1 0 0); + --backgrdound: oklch(1 0 0); --foreground: oklch(0.141 0.005 285.823); --card: oklch(1 0 0); --card-foreground: oklch(0.141 0.005 285.823); diff --git a/src/constants/eme.ts b/src/constants/eme.ts new file mode 100644 index 0000000..036c75f --- /dev/null +++ b/src/constants/eme.ts @@ -0,0 +1,20 @@ +export interface Message { + id: string; + text: string; + sender: 'user' | 'bot'; + timestamp: Date; +} + +export const botDummy: Message = { + id: "2", + text: "CS336 is generally considered to be a challenging course. Students have mentioned that it involves a mix of coding and proofs, which can make it harder compared to other classes. However, many also note that if you engage with the material and focus on understanding concepts like greedy algorithms and dynamic programming, you can gain a solid grasp of these important topics. It's recommended to take it if you're looking to prepare for technical interviews, but be prepared for the workload and complexity. If you're unsure, you might want to ask others about their specific experiences or tips for succeeding in the course.", + sender: 'bot', + timestamp: new Date() +}; + +export const userDummy: Message = { + id: "1", + text: "Is CS336 a difficult course?", + sender: 'user', + timestamp: new Date() +}; \ No newline at end of file From 7ebc861de9efddf176a87786c8f423f70218b66a Mon Sep 17 00:00:00 2001 From: Keigo Healy Date: Sat, 27 Sep 2025 17:07:27 -0500 Subject: [PATCH 2/8] ui should be done --- src/app/eme/page.tsx | 84 +++++++++++++++------------- src/components/navigation/header.tsx | 6 ++ 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/app/eme/page.tsx b/src/app/eme/page.tsx index 0a69305..cb57f8c 100644 --- a/src/app/eme/page.tsx +++ b/src/app/eme/page.tsx @@ -14,61 +14,65 @@ export default function emePage() { const submitPrompt = async (e: React.FormEvent) => { if (isLoading || prompt == "") return; - + //add logic here for submission } return ( -
-

+
+

eme

eme is the Emerging Coders chatbot that answers your questions about navigating CS at Northwestern. Answers are based on EMCO GroupMe messages.

-
- {messages.map((msg, i) => ( -
+
+ {messages.map((msg, i) => ( +
-
-
- {msg.text} +
+
+ {msg.text} +
-
- ))} -
+ ))} +
-
-
- setPrompt(e.target.value)} - className="w-full bg-black border-zinc-800 text-white focus-visible:ring-purple-500/30 px-5 py-2" - /> - -
+
+
+ setPrompt(e.target.value)} + className="w-full bg-black border-zinc-800 text-white focus-visible:ring-purple-500/30 px-5 py-2 rounded focus:outline-none" + disabled={isLoading} + /> + +
+
) diff --git a/src/components/navigation/header.tsx b/src/components/navigation/header.tsx index cd1a443..8b698ec 100644 --- a/src/components/navigation/header.tsx +++ b/src/components/navigation/header.tsx @@ -242,6 +242,12 @@ export default function Header() { + + EME + Date: Sat, 27 Sep 2025 17:15:07 -0500 Subject: [PATCH 3/8] done with the ui --- src/app/eme/page.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/app/eme/page.tsx b/src/app/eme/page.tsx index cb57f8c..d07b8bc 100644 --- a/src/app/eme/page.tsx +++ b/src/app/eme/page.tsx @@ -1,6 +1,7 @@ "use client" import { useState, useEffect } from "react"; import { Message, botDummy, userDummy } from "@/constants/eme"; +import { timeStamp } from "console"; export default function emePage() { const [messages, setMessages] = useState([userDummy, botDummy, userDummy, botDummy, userDummy, botDummy]) @@ -14,6 +15,18 @@ export default function emePage() { const submitPrompt = async (e: React.FormEvent) => { if (isLoading || prompt == "") return; + + const msg: Message = { + id: Date.now().toString(), + text: prompt, + sender: 'user', + timestamp: new Date() + } + + setMessages(prev => [...prev, msg]) + setPrompt("") + setIsLoading(true) + //add logic here for submission } @@ -26,7 +39,7 @@ export default function emePage() { eme is the Emerging Coders chatbot that answers your questions about navigating CS at Northwestern. Answers are based on EMCO GroupMe messages.

-
+
{messages.map((msg, i) => (
Date: Sat, 27 Sep 2025 19:20:53 -0500 Subject: [PATCH 4/8] endpoint created --- src/app/eme/fetch-eme.ts | 24 +++++++++++++++++++++ src/app/eme/page.tsx | 46 +++++++++++++++++++++++++++------------- 2 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 src/app/eme/fetch-eme.ts diff --git a/src/app/eme/fetch-eme.ts b/src/app/eme/fetch-eme.ts new file mode 100644 index 0000000..7b88031 --- /dev/null +++ b/src/app/eme/fetch-eme.ts @@ -0,0 +1,24 @@ +import { useEffect, useState } from "react"; + +export interface UseEmeResult { + generation: string, + isLoading: boolean, + isError: boolean, +} + +export async function fetchEme(prompt: string): Promise<{ generation: string }> { + + return {generation: "eme default test output :)"}; + + const res = await fetch('/api/eme', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ prompt }), + }); + + if (!res.ok) throw new Error(`eme responded with status ${res.status}`); + + const body = await res.json(); + const generation = typeof body === 'string' ? body : body?.generation ?? String(body); + return { generation }; +} \ No newline at end of file diff --git a/src/app/eme/page.tsx b/src/app/eme/page.tsx index d07b8bc..352df89 100644 --- a/src/app/eme/page.tsx +++ b/src/app/eme/page.tsx @@ -1,33 +1,46 @@ "use client" import { useState, useEffect } from "react"; import { Message, botDummy, userDummy } from "@/constants/eme"; -import { timeStamp } from "console"; +import { fetchEme } from "./fetch-eme"; export default function emePage() { const [messages, setMessages] = useState([userDummy, botDummy, userDummy, botDummy, userDummy, botDummy]) const [prompt, setPrompt] = useState("") const [isLoading, setIsLoading] = useState(false) - useEffect(() => { + // useEffect(() => { - }, []) + // }, []) const submitPrompt = async (e: React.FormEvent) => { - if (isLoading || prompt == "") - return; + e.preventDefault(); + if (isLoading || prompt.trim() === "") return; - const msg: Message = { + const userMsg: Message = { id: Date.now().toString(), - text: prompt, - sender: 'user', - timestamp: new Date() - } + text: prompt, + sender: "user", + timestamp: new Date(), + }; - setMessages(prev => [...prev, msg]) + setMessages(prev => [...prev, userMsg]); + setIsLoading(true); setPrompt("") - setIsLoading(true) - //add logic here for submission + try { + const { generation } = await fetchEme(prompt); + const botMsg: Message = { + id: Date.now().toString(), + text: generation, + sender: "bot", + timestamp: new Date(), + }; + setMessages(prev => [...prev, botMsg]); + } catch (error) { + console.error("Error retrieving eme output:", error); + } finally { + setIsLoading(false); + } } return ( @@ -35,8 +48,11 @@ export default function emePage() {

eme

-

- eme is the Emerging Coders chatbot that answers your questions about navigating CS at Northwestern. Answers are based on EMCO GroupMe messages. +

+ eme is the EMCO chatbot that answers your questions about navigating CS at Northwestern +

+

+ Answers based on historical EMCO GroupMe messages

From 71409f16b3391a099e14536d41f4a9c2d3ba36b7 Mon Sep 17 00:00:00 2001 From: Keigo Healy Date: Mon, 29 Sep 2025 09:47:31 -0500 Subject: [PATCH 5/8] about section --- package-lock.json | 655 ++++++++++++++++++++++++++++++++++ package.json | 5 +- src/app/eme/page.tsx | 25 +- src/components/ui/popover.tsx | 48 +++ 4 files changed, 725 insertions(+), 8 deletions(-) create mode 100644 src/components/ui/popover.tsx diff --git a/package-lock.json b/package-lock.json index e2dc188..cc9e105 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-navigation-menu": "^1.2.5", + "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.3", "class-variance-authority": "^0.7.1", @@ -317,6 +318,44 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", + "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", + "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.3", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz", + "integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -981,6 +1020,85 @@ } } }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collapsible": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.3.tgz", @@ -1250,6 +1368,453 @@ } } }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", @@ -1425,6 +1990,39 @@ } } }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-escape-keydown": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", @@ -1470,6 +2068,57 @@ } } }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-visually-hidden": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", @@ -1492,6 +2141,12 @@ } } }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", diff --git a/package.json b/package.json index ec0b760..ee03a2a 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-navigation-menu": "^1.2.5", + "@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tabs": "^1.1.3", "class-variance-authority": "^0.7.1", @@ -35,13 +36,13 @@ "devDependencies": { "@eslint/eslintrc": "^3", "@tailwindcss/postcss": "^4", + "@trivago/prettier-plugin-sort-imports": "^5.2.0", "@types/node": "^20", "@types/react": "19.0.12", "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "15.2.1", "tailwindcss": "^4", - "typescript": "5.8.2", - "@trivago/prettier-plugin-sort-imports": "^5.2.0" + "typescript": "5.8.2" } } diff --git a/src/app/eme/page.tsx b/src/app/eme/page.tsx index 352df89..bddfdde 100644 --- a/src/app/eme/page.tsx +++ b/src/app/eme/page.tsx @@ -2,6 +2,8 @@ import { useState, useEffect } from "react"; import { Message, botDummy, userDummy } from "@/constants/eme"; import { fetchEme } from "./fetch-eme"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Button } from "@/components/ui/button"; export default function emePage() { const [messages, setMessages] = useState([userDummy, botDummy, userDummy, botDummy, userDummy, botDummy]) @@ -49,12 +51,23 @@ export default function emePage() { eme

- eme is the EMCO chatbot that answers your questions about navigating CS at Northwestern + Ask me something like "should I take CS214 and CS211 at the same time?"

-

- Answers based on historical EMCO GroupMe messages -

- + + + + + +
+

+ eme is the EMCO chatbot that answers your questions about navigating CS at Northwestern. +

+

+ Answers are based on historical EMCO GroupMe messages. +

+
+
+
{messages.map((msg, i) => ( @@ -95,7 +108,7 @@ export default function emePage() { />
-
+
{messages.map((msg, i) => (
Date: Wed, 1 Oct 2025 19:15:00 -0500 Subject: [PATCH 7/8] finished eme --- src/app/eme/chat-bubble.tsx | 24 ++++++++++++ src/app/eme/fetch-eme.ts | 6 +-- src/app/eme/page.tsx | 77 +++++++++++++++++++------------------ src/constants/eme.ts | 15 ++++++-- 4 files changed, 78 insertions(+), 44 deletions(-) create mode 100644 src/app/eme/chat-bubble.tsx diff --git a/src/app/eme/chat-bubble.tsx b/src/app/eme/chat-bubble.tsx new file mode 100644 index 0000000..608f1b7 --- /dev/null +++ b/src/app/eme/chat-bubble.tsx @@ -0,0 +1,24 @@ +import { Message } from "@/constants/eme" +export function ChatBubble({ msg, index }: {msg: Message, index: number}) { + return ( +
+
+
+ {msg.text} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/app/eme/fetch-eme.ts b/src/app/eme/fetch-eme.ts index 393502e..c2db9dd 100644 --- a/src/app/eme/fetch-eme.ts +++ b/src/app/eme/fetch-eme.ts @@ -6,12 +6,12 @@ export interface UseEmeResult { isError: boolean, } -export async function fetchEmeResponse(prompt: string): Promise> { +export async function fetchEmeResponse(message: string): Promise> { const response = await fetch(`${EME_API_BASE_URL}/chat`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ prompt }), + body: JSON.stringify({ message }), }); if (!response.ok) throw new Error(`eme responded with status ${response.status}`); @@ -26,7 +26,7 @@ export async function fetchEmeResponse(prompt: string): Promise { - const response = await fetch(`${EME_API_BASE_URL}/heath`); + const response = await fetch(`${EME_API_BASE_URL}/health`); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); diff --git a/src/app/eme/page.tsx b/src/app/eme/page.tsx index e04cea7..fe72ea0 100644 --- a/src/app/eme/page.tsx +++ b/src/app/eme/page.tsx @@ -1,17 +1,18 @@ "use client" import { useState } from "react"; -import { Message, botDummy, userDummy } from "@/constants/eme"; +import { Message, thinkingMessage } from "@/constants/eme"; import { fetchEmeResponse, fetchEmeHealth } from "./fetch-eme"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Button } from "@/components/ui/button"; +import { ChatBubble } from "./chat-bubble"; export default function emePage() { - const [messages, setMessages] = useState([userDummy, botDummy]) + const [messages, setMessages] = useState([]) const [prompt, setPrompt] = useState("") const [isLoading, setIsLoading] = useState(false) const [isStreaming, setIsStreaming] = useState(false) - const isHealthCheck = true; + const isHealthCheck = false; const submitPrompt = async (e: React.FormEvent) => { e.preventDefault(); @@ -38,7 +39,7 @@ export default function emePage() { const stream = await fetchEmeResponse(prompt); const reader = stream.getReader(); const decoder = new TextDecoder(); - + let botResponse = ''; const botMsg: Message = { id: Date.now().toString(), @@ -46,15 +47,15 @@ export default function emePage() { sender: "bot", timestamp: new Date(), }; - + + setIsStreaming(true); setMessages(prev => [...prev, botMsg]); setIsLoading(false); - setIsStreaming(true); - + while (true) { const { done, value} = await reader.read(); if (done) break; - + const chunk = decoder.decode(value, { stream: true}); botResponse += chunk; @@ -89,54 +90,56 @@ export default function emePage() { } return ( -
+

eme

-

- Ask me something like "should I take CS214 and CS211 at the same time?" -

- -
+ +

- eme is the EMCO chatbot that answers your questions about navigating CS at Northwestern. + eme is the EMCO chatbot that answers your questions about navigating CS at Northwestern. We hope it can be another point of reference for underclass students getting started.

- Answers are based on historical EMCO GroupMe messages. + Answers are based on historical EMCO GroupMe messages. eme uses {" "} + + RAG + + {" "} to pull relevant messages into its context. +

+

+ NOTE: eme can make mistakes. Emerging Coders, its affiliated members and Executive Board do not endorse its messages.

+
-
+
+
+ {messages.length === 0 && +

+ Ask me something like "should I take CS214 and CS211 at the same time?" +

+ } +
{messages.map((msg, i) => ( -
-
-
- {msg.text} -
-
-
+ ))} + {isLoading && + + }
-
+
Date: Wed, 1 Oct 2025 19:18:37 -0500 Subject: [PATCH 8/8] lint correcting --- src/app/eme/{fetch-eme.ts => fetch-eme.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/app/eme/{fetch-eme.ts => fetch-eme.tsx} (100%) diff --git a/src/app/eme/fetch-eme.ts b/src/app/eme/fetch-eme.tsx similarity index 100% rename from src/app/eme/fetch-eme.ts rename to src/app/eme/fetch-eme.tsx