diff --git a/frontend/public/docs/beta.md b/frontend/public/docs/beta.md index a762137..f0a9200 100644 --- a/frontend/public/docs/beta.md +++ b/frontend/public/docs/beta.md @@ -4,12 +4,12 @@ First, what works: 1. There are two personas populated: 1. One is me [jketreno](/u/jketreno) - 2. The other is a ficticious AI generated persona named [Eliz](/u/eliza). -2. **Candidate Skill Chat** You can go to the Chat tab to ask questions about the active candaite. + 2. The other is a ficticious AI generated persona named [Eliza](/u/eliza). +2. **Chat** You can go to the Chat tab to ask questions about the active candaite. 3. **Resume Builder** You can build a resume for a person given a Job Description What doesn't work: 1. User login, registration, etc. 2. Lots of the links on the site. -3. Anything that isn't "Chat", "Resume Builder", or "About". +3. Basically.. anything that isn't "Chat", "Resume Builder", or "About". diff --git a/frontend/public/profile.png b/frontend/public/profile.png new file mode 100755 index 0000000..a633b87 Binary files /dev/null and b/frontend/public/profile.png differ diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a7981ff..a0045a9 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,43 +1,6 @@ -import React, { useRef, useCallback } from 'react'; -import { BrowserRouter as Router, Routes, Route, useLocation } from "react-router-dom"; -import { SessionWrapper } from "./App/SessionWrapper"; -import { Main } from "./App/Main"; +import React from 'react'; +import { BrowserRouter as Router } from "react-router-dom"; import { BackstoryApp } from './NewApp/BackstoryApp'; -import { Snack, SeverityType } from './Components/Snack'; - -const PathRouter = ({ setSnack }: { setSnack: any }) => { - const location = useLocation(); - const segments = location.pathname.split("/").filter(Boolean); - const sessionId = segments[segments.length - 1]; - - return ( - -
- - ); -} - -function App2() { - const snackRef = useRef(null); - - const setSnack = useCallback((message: string, severity?: SeverityType) => { - snackRef.current?.setSnack(message, severity); - }, [snackRef]); - - return ( - <> - - - } /> - - - - - ); -} - const App = () => { return ( diff --git a/frontend/src/Components/StyledMarkdown.tsx b/frontend/src/Components/StyledMarkdown.tsx index 3b8e853..d23e30f 100644 --- a/frontend/src/Components/StyledMarkdown.tsx +++ b/frontend/src/Components/StyledMarkdown.tsx @@ -69,7 +69,7 @@ const StyledMarkdown: React.FC = (props: StyledMarkdownProp return
{content}
}; } - return
{element.children}
; + return
{element.children}
; }, }, a: { @@ -81,7 +81,7 @@ const StyledMarkdown: React.FC = (props: StyledMarkdownProp if (href) { if (href.match(/^\//)) { event.preventDefault(); - window.history.replaceState({}, '', `${href}/${sessionId}`); + window.history.replaceState({}, '', `${href}`); } } }, diff --git a/frontend/src/NewApp/BackstoryApp.css b/frontend/src/NewApp/BackstoryApp.css new file mode 100644 index 0000000..8f0ac56 --- /dev/null +++ b/frontend/src/NewApp/BackstoryApp.css @@ -0,0 +1,257 @@ +div { + box-sizing: border-box; + overflow-wrap: break-word; + word-break: break-word; +} + +.gl-container #scene { + top: 0px !important; + left: 0px !important; +} + +pre { + max-width: 100%; + max-height: 100%; + overflow: auto; + white-space: pre-wrap; + box-sizing: border-box; + border: 3px solid #E0E0E0; +} + +button { + overflow-wrap: initial; + word-break: initial; +} + +.TabPanel { + display: flex; + height: 100%; +} + +.MuiToolbar-root .MuiBox-root { + border-bottom: none; +} + +.MuiTabs-root .MuiTabs-indicator { + background-color: orange; +} + +.SystemInfo { + display: flex; + flex-direction: column; + gap: 5px; + padding: 5px; + flex-grow: 1; +} + +.SystemInfoItem { + display: flex; /* Grid for individual items */ + flex-direction: row; + flex-grow: 1; +} + +.SystemInfoItem > div:first-child { + display: flex; + justify-self: end; /* Align the first column content to the right */ + width: 10rem; +} + +.SystemInfoItem > div:last-child { + display: flex; + flex-grow: 1; + justify-self: end; /* Align the first column content to the right */ +} + +.DocBox { + display: flex; + flex-direction: column; + flex-grow: 1; + max-width: 2048px; + margin: 0 auto; +} + +.Controls { + display: flex; + background-color: #F5F5F5; + border: 1px solid #E0E0E0; + overflow-y: auto; + padding: 10px; + flex-direction: column; + margin-left: 10px; + box-sizing: border-box; + overflow-x: visible; + min-width: 10rem; + flex-grow: 1; +} + +.MessageContent div > p:first-child { + margin-top: 0; +} + +.MenuCard.MuiCard-root { + display: flex; + flex-direction: column; + min-width: 10rem; + flex-grow: 1; + background-color: #1A2536; /* Midnight Blue */ + color: #D3CDBF; /* Warm Gray */ + border-radius: 0; +} + +.MenuCard.MuiCard-root button { + min-height: 64px; +} +/* Prevent toolbar from shrinking vertically when media < 600px */ +.MuiToolbar-root { + min-height: 72px !important; + padding-left: 16px !important; + padding-right: 16px !important; +} + +.ChatBox { + display: flex; + flex-direction: column; + flex-grow: 1; + max-width: 1024px; + width: 100%; + margin: 0 auto; + background-color: #D3CDBF; +} + +.user-message.MuiCard-root { + background-color: #DCF8C6; + border: 1px solid #B2E0A7; + color: #333333; + margin-bottom: 0.75rem; + margin-left: 1rem; + border-radius: 0.25rem; + min-width: 80%; + max-width: 80%; + justify-self: right; + display: flex; + white-space: pre-wrap; + overflow-wrap: break-word; + word-break: break-word; + flex-direction: column; + align-items: self-end; + align-self: end; + flex-grow: 0; +} + +.About.MuiCard-root, +.assistant-message.MuiCard-root { + border: 1px solid #E0E0E0; + background-color: #FFFFFF; + color: #333333; + margin-bottom: 0.75rem; + margin-right: 1rem; + min-width: 70%; + border-radius: 0.25rem; + justify-self: left; + display: flex; + white-space: pre-wrap; + overflow-wrap: break-word; + word-break: break-word; + flex-direction: column; + flex-grow: 0; + padding: 16px 0; + font-size: 0.9rem; +} + + +.About.MuiCard-root { + display: flex; + flex-grow: 1; + width: 100%; + margin-left: 0; + margin-right: 0; +} + +.About .MuiCardContent-root, +.assistant-message .MuiCardContent-root { + padding: 0 16px !important; + font-size: 0.9rem; +} + +.About span, +.assistant-message span { + font-size: 0.9rem; +} + +.user-message .MuiCardContent-root:last-child, +.assistant-message .MuiCardContent-root:last-child, +.About .MuiCardContent-root:last-child { + padding: 16px; +} + +.users > div { + padding: 0.25rem; +} + +.user-active { + font-weight: bold; +} + +.metadata { + border: 1px solid #E0E0E0; + font-size: 0.75rem; + padding: 0.125rem; +} + +/* Reduce general whitespace in markdown content */ +* p.MuiTypography-root { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + font-size: 0.9rem; +} + +/* Reduce space between headings and content */ +* h1.MuiTypography-root, +* h2.MuiTypography-root, +* h3.MuiTypography-root, +* h4.MuiTypography-root, +* h5.MuiTypography-root, +* h6.MuiTypography-root { + margin-top: 1rem; + margin-bottom: 0.5rem; + font-size: 1rem; +} + +/* Reduce space in lists */ +* ul.MuiTypography-root, +* ol.MuiTypography-root { + margin-top: 0.5rem; + margin-bottom: 0.5rem; + font-size: 0.9rem; +} + +* li.MuiTypography-root { + margin-bottom: 0.25rem; + font-size: 0.9rem; +} + +* .MuiTypography-root li { + margin-top: 0; + margin-bottom: 0; + padding: 0; + font-size: 0.9rem; +} + +/* Reduce space around code blocks */ +* .MuiTypography-root pre { + border: 1px solid #F5F5F5; + border-radius: 0.5rem; + padding: 0.5rem 0.75rem; + margin-top: 0; + margin-bottom: 0; + font-size: 0.9rem; +} + +.PromptStats .MuiTableCell-root { + font-size: 0.8rem; +} + +#SystemPromptInput { + font-size: 0.9rem; + line-height: 1.25rem; +} \ No newline at end of file diff --git a/frontend/src/NewApp/BackstoryApp.tsx b/frontend/src/NewApp/BackstoryApp.tsx index 9e9645c..f9a2c65 100644 --- a/frontend/src/NewApp/BackstoryApp.tsx +++ b/frontend/src/NewApp/BackstoryApp.tsx @@ -28,19 +28,21 @@ import { ConversationHandle } from '../Components/Conversation'; import { HomePage } from './Pages/HomePage'; -import { HomePage as ChatPage } from '../Pages/HomePage'; +import { ChatPage } from './Pages/ChatPage'; import { ResumeBuilderPage } from '../Pages/ResumeBuilderPage'; // import { BackstoryThemeVisualizer } from './BackstoryThemeVisualizer'; import { AboutPage } from './Pages/AboutPage'; import { BetaPage } from './Pages/BetaPage'; import { CreateProfilePage } from './Pages/CreateProfilePage'; +import { VectorVisualizerPage } from 'Pages/VectorVisualizerPage'; +import './BackstoryApp.css'; import '@fontsource/roboto/300.css'; import '@fontsource/roboto/400.css'; import '@fontsource/roboto/500.css'; import '@fontsource/roboto/700.css'; -import { VectorVisualizerPage } from 'Pages/VectorVisualizerPage'; -import { calculatePoint } from 'mermaid/dist/utils'; + +import { connectionBase } from '../Global'; type NavigationLinkType = { name: string; @@ -153,6 +155,20 @@ const BackstoryPageContainer = (props : BackstoryPageContainerProps) => { ); } + +// Cookie handling functions +const getCookie = (name: string) => { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + if (parts.length === 2) return parts.pop()?.split(';').shift(); + return null; +}; + +const setCookie = (name: string, value: string, days = 7) => { + const expires = new Date(Date.now() + days * 864e5).toUTCString(); + document.cookie = `${name}=${value}; expires=${expires}; path=/; SameSite=Strict`; +}; + const BackstoryApp = () => { const navigate = useNavigate(); const location = useLocation(); @@ -161,6 +177,7 @@ const BackstoryApp = () => { const [navigationLinks, setNavigationLinks] = useState([]); const snackRef = useRef(null); const chatRef = useRef(null); + const [sessionId, setSessionId] = useState(undefined); const setSnack = useCallback((message: string, severity?: SeverityType) => { snackRef.current?.setSnack(message, severity); }, [snackRef]); @@ -170,6 +187,79 @@ const BackstoryApp = () => { navigate('/chat'); }; const [page, setPage] = useState(""); + const [storeInCookie, setStoreInCookie] = useState(true); + + // Extract session ID from URL query parameter or cookie + const urlParams = new URLSearchParams(window.location.search); + const urlSessionId = urlParams.get('id'); + const cookieSessionId = getCookie('session_id'); + + // Fetch or join session on mount + useEffect(() => { + const fetchSession = async () => { + try { + let response; + let newSessionId; + if (urlSessionId) { + // Attempt to join session from URL + response = await fetch(`${connectionBase}/join-session/${urlSessionId}`, { + credentials: 'include', + }); + if (!response.ok) { + throw new Error('Session not found'); + } + newSessionId = (await response.json()).id; + } else if (cookieSessionId) { + // Attempt to join session from cookie + response = await fetch(`${connectionBase}/api/join-session/${cookieSessionId}`, { + credentials: 'include', + }); + if (!response.ok) { + // Cookie session invalid, create new session + response = await fetch(`${connectionBase}/api/create-session`, { + method: 'POST', + credentials: 'include', + }); + if (!response.ok) { + throw new Error('Failed to create session'); + } + } + newSessionId = (await response.json()).id; + } else { + // Create a new session + response = await fetch(`${connectionBase}/api/create-session`, { + method: 'POST', + credentials: 'include', + }); + if (!response.ok) { + throw new Error('Failed to create session'); + } + newSessionId = (await response.json()).id; + } + setSessionId(newSessionId); + // Store in cookie if user opts in + if (storeInCookie) { + setCookie('session_id', newSessionId); + } + // Update URL without reloading + if (!storeInCookie || (urlSessionId && urlSessionId !== newSessionId)) { + window.history.replaceState(null, '', `?id=${newSessionId}`); + } + } catch (err) { + setSnack("" + err); + } + }; + fetchSession(); + }, []); + + const copyLink = () => { + const link = `${window.location.origin}${window.location.pathname}?id=${sessionId}`; + navigator.clipboard.writeText(link).then(() => { + alert('Link copied to clipboard!'); + }).catch(() => { + alert('Failed to copy link'); + }); + }; useEffect(() => { const currentRoute = location.pathname.split("/")[1] ? `/${location.pathname.split("/")[1]}` : "/"; @@ -193,42 +283,44 @@ const BackstoryApp = () => { minHeight: "calc(100vh - 72px)", }}> - - } /> - } /> - } /> - } /> - } /> + {sessionId !== undefined && + + } /> + } /> + } /> + } /> + } /> } /> - } /> - }/> - {/* Candidate-specific routes */} - {user.type === 'candidate' && ( - <> - } /> - } /> - } /> - } /> - - )} - - {/* Employer-specific routes */} - {user.type === 'employer' && ( - <> - } /> - } /> - } /> - } /> - - )} - - {/* Common routes */} - } /> - } /> - - {/* Redirect to BETA by default */} - } /> - + } /> + } /> + {/* Candidate-specific routes */} + {user.type === 'candidate' && ( + <> + } /> + } /> + } /> + } /> + + )} + + {/* Employer-specific routes */} + {user.type === 'employer' && ( + <> + } /> + } /> + } /> + } /> + + )} + + {/* Common routes */} + } /> + } /> + + {/* Redirect to BETA by default */} + } /> + + } {location.pathname === "/" &&