# Project export: LockedIn

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Your personal AI assistant to help you ace job interviews.
- Devpost: https://devpost.com/software/lockedin-1d2om6
- GitHub: https://github.com/cjaeey/part-time-job_manager-empathic-voice-interface-starter
- Team: 1 GitHub contributor(s) — cjaeey (13 commits)

## Devpost submission (written by the team)

### Inspiration

We saw a lot of people at the event hiring and attended the Hume AI workshop so we put 2 and 2 together

### What it does

We built an AI LLM Interview Simulation

### How we built it

We utilized Prompt Engineering and Next.js framework.

### Challenges we ran into

Learning Next.js and integrating languages we already know.

### Accomplishments we're proud of

Making the button to connect the front to the back end and navigating pages

### What we learned

We learned how to navigate github and version control as a team for the first time

### What's next

We hope to spread the idea to get people more interested. Potentially if we gain more experience we can launch LockedIn in a bigger scale.

## README (from the GitHub repository)

<div align="center">
  <h1>Voice Interface Example</h1>
</div>

## Overview

This project features a sample implementation of Hume's [Empathic Voice Interface](https://hume.docs.buildwithfern.com/docs/empathic-voice-interface-evi/overview) using Hume's React SDK. Here, we have a simple EVI that uses the Next.js App Router.

## Project deployment

Click the button below to deploy this example project with Vercel:

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fhumeai%2Fhume-evi-next-js-starter&env=HUME_API_KEY,HUME_SECRET_KEY)

Below are the steps to completing the process to start:

1. Clone Git Repository
2. Provide the required environment variables. To get your API key and Client Secret key, log into the portal and visit the [API keys page](https://beta.hume.ai/settings/keys).
3. Create a .env file that includes the API & Client Secret Key & Config ID Entered as below:
HUME_API_KEY=
HUME_SECRET_KEY=
NEXT_PUBLIC_HUME_CONFIG_ID=
4. Open Terminal and type pnpm i
5. On the same Terminal type pnpm dev and you're all set.

## Support

If you have questions, require assistance, or wish to engage in discussions pertaining to this starter template, [please reach out to Hume on Discord](https://link.hume.ai/discord).


## Detected evidence (automated analysis)

Indexed codebase: 29 recognized source files, 177 KB.
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Google Gemini (technology) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (33 of 33)

```
.gitignore
app/chat/page.tsx
app/error.tsx
app/globals.css
app/layout.tsx
components.json
components/Chat.tsx
components/Controls.tsx
components/Expressions.tsx
components/logos/GitHub.tsx
components/logos/Hume.tsx
components/Messages.tsx
components/MicFFT.tsx
components/Nav.tsx
components/Partners.js
components/StartCall.tsx
components/styles.css
components/ui/button.tsx
components/ui/toggle.tsx
next-env.d.ts
package.json
pages/index.js
pages/results.js
pages/tips.js
postcss.config.mjs
public/style.css
README.md
tailwind.config.ts
tsconfig.json
utils/expressionColors.ts
utils/expressionLabels.ts
utils/getHumeAccessToken.ts
utils/index.ts
```

### Dependencies

- package.json: @humeai/voice-react@^0.1.15, @radix-ui/react-slot@^1.0.2, @radix-ui/react-switch@^1.0.3, @radix-ui/react-toggle@^1.0.3, @types/node@^20, @types/react@^18, @types/react-dom@^18, @types/react-virtualized@^9.21.30, class-variance-authority@^0.7.0, clsx@^2.1.1, eslint@^8, eslint-config-next@14.2.3, framer-motion@^11.2.10, geist@^1.3.0, hume@^0.8.10, lucide-react@^0.394.0, next@^14.2.15, postcss@^8, react@^18, react-dom@^18, react-virtualized@^9.22.5, remeda@^2.0.9, server-only@^0.0.1, tailwind-merge@^2.3.0, tailwindcss@^3.4.1, tailwindcss-animate@^1.0.7, ts-pattern@^5.1.2, typescript@^5

### Recent commits (newest first)

- Update README.md
- test
- Merge branch 'main' of https://github.com/cjaeey/part-time-job_manager-empathic-voice-interface-starter
- deleted file
- restart cuz we stuid
- cool
- restart cuz we stuid
- more content
- updating file base
- vid_test
- other pages
- updates
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### package.json

```
{
  "name": "hume-evi-next-js-starter",
  "version": "0.1.0",
  "private": true,
  "homepage": "https://github.com/humeai/hume-evi-next-js-starter",
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@humeai/voice-react": "^0.1.15",
    "@radix-ui/react-slot": "^1.0.2",
    "@radix-ui/react-switch": "^1.0.3",
    "@radix-ui/react-toggle": "^1.0.3",
    "@types/react-virtualized": "^9.21.30",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "framer-motion": "^11.2.10",
    "geist": "^1.3.0",
    "hume": "^0.8.10",
    "lucide-react": "^0.394.0",
    "next": "^14.2.15",
    "react": "^18",
    "react-dom": "^18",
    "react-virtualized": "^9.22.5",
    "remeda": "^2.0.9",
    "server-only": "^0.0.1",
    "tailwind-merge": "^2.3.0",
    "tailwindcss-animate": "^1.0.7",
    "ts-pattern": "^5.1.2"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "eslint": "^8",
    "eslint-config-next": "14.2.3",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### utils/index.ts

```typescript
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import "./globals.css";
import { Nav } from "@/components/Nav";
import { cn } from "@/utils";

export const metadata: Metadata = {
  title: "Hume AI - EVI - Next.js Starter",
  description: "A Next.js starter using Hume AI's Empathic Voice Interface",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={cn(
          GeistSans.variable,
          GeistMono.variable,
          "flex flex-col min-h-screen"
        )}
      >
        <Nav />
        {children}
      </body>
    </html>
  );
}

```

### pages/index.js

```javascript
import Head from 'next/head';
import { useEffect } from 'react';
import { useRouter } from 'next/router'; // Import useRouter

export default function Home() {
  const router = useRouter(); // Initialize useRouter

  useEffect(() => {
    const showWelcomeMessage = () => {
      const welcomeMessage = document.createElement("div");
      welcomeMessage.className = "alert alert-info";
      welcomeMessage.innerText =
        "Welcome! Let's get ready for your next job interview!";
      document.body.insertBefore(welcomeMessage, document.body.firstChild);

      setTimeout(() => {
        welcomeMessage.remove();
      }, 5000);
    };
    showWelcomeMessage();
  }, []);

  const talkToAI = () => {
    router.push('/chat'); // Navigate to the chat page
  };

  return (
    <>
      <Head>
        <title>LockedIn - Home</title>
        <meta charSet="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="stylesheet" href="/style.css" />
        <link
          href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
          rel="stylesheet"
          integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
          crossOrigin="anonymous"
        />
      </Head>

      <nav className="navbar navbar-expand-lg navbar-light bg-light">
        <div className="container-fluid">
          <a className="navbar-brand" href="/">LockedIn</a>
          <button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
            aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span className="navbar-toggler-icon"></span>
          </button>
          <div className="collapse navbar-collapse" id="navbarNav">
            <ul className="navbar-nav ms-auto">
              <li className="nav-item">
                <a className="nav-link" href="/">Home</a>
              </li>
              <li className="nav-item">
                <a className="nav-link" href="/results">Personal Results</a>
              </li>
              <li className="nav-item">
                <a className="nav-link" href="/tips">General Tips</a>
              </li>
            </ul>
          </div>
        </div>
      </nav>

      <div className="container text-center mt-4">
        <div className="video-container">
          <video controls className="w-100" style={{ maxHeight: '400px' }}>
          <source src="bdb.mp4" type="video/mp4" />
                      Your browser does not support the video tag.
          </video>
        </div>

        <h1 className="mt-4">Welcome to LockedIn AI Coach!</h1>
        <p>Your personal AI assistant to help you ace job interviews.</p>
        <button id="talkToAI" className="btn btn-primary hover-effect" onClick={talkToAI}>
          Talk to AI
        </button>
        <br></br>
        <h3 className="mt-5">About</h3>
      
        <p>
          Struggling with getting job interviews? Try our interview simulator. See
          if you can pass the challenge. Speak without stuttering (no saying "um",
          "but", "uh", "like", "well", "and yeah"). Speak fluidly. Talk to our sample model.
          We decided to create this due to the amount of recruitments people are aiming for.
          Job interviews can be super nerve-wracking! That’s why we created 
          LockedIn, (Inspired by Linkedin) an AI coach powered by Hume AI that lets you practice with a virtual interviewer. 
          Our platform makes it easy and fun to sharpen your skills, get feedback, and build your 
          confidence—all while simulating real interview situations. We believe everyone deserves 
          a shot at their dream job, and with our AI-powered coach, we’re here to help you get there. 
          Let’s ace those interviews together!
        </p>

        <h3 className="mt-5">How to Use</h3>
        <ol className="text-start mx-auto" style={{ maxWidth: '400px' }}>
          <li>Watch the video</li>
          <li>Practice an interview with our Sample Model</li>
          <li>Compare your score</li>
          <li>Use results to engrain into your subconscious and try again</li>
        </ol>

        <div id="feedback" className="feedback mt-4"></div>
      </div>

      <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
    </>
  );
}

```

### app/chat/page.tsx

```typescript
import { getHumeAccessToken } from "@/utils/getHumeAccessToken";
import dynamic from "next/dynamic";

const Chat = dynamic(() => import("@/components/Chat"), {
  ssr: false,
});

export default async function ChatPage() {
  const accessToken = await getHumeAccessToken();

  if (!accessToken) {
    throw new Error();
  }

  return (
    <div className={"grow flex flex-col"}>
      <Chat accessToken={accessToken} />
    </div>
  );
}
```

### next-env.d.ts

```typescript
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.

```

### tailwind.config.ts

```typescript
import type { Config } from "tailwindcss"
import defaultTheme from 'tailwindcss/defaultTheme'

const config = {
  darkMode: ["class"],
  content: [
    './pages/**/*.{ts,tsx}',
    './components/**/*.{ts,tsx}',
    './app/**/*.{ts,tsx}',
    './src/**/*.{ts,tsx}',
	],
  prefix: "",
  theme: {
    fontFamily: {
      'sans': ['var(--font-geist-sans)', ...defaultTheme.fontFamily.sans],
      'mono': ['var(--font-geist-mono)', ...defaultTheme.fontFamily.mono],
    },
    container: {
      center: true,
      padding: "2rem",
      screens: {
        "2xl": "1400px",
      },
    },
    extend: {
      colors: {
        border: "hsl(var(--border))",
        input: "hsl(var(--input))",
        ring: "hsl(var(--ring))",
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        secondary: {
          DEFAULT: "hsl(var(--secondary))",
          foreground: "hsl(var(--secondary-foreground))",
        },
        destructive: {
          DEFAULT: "hsl(var(--destructive))",
          foreground: "hsl(var(--destructive-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
        accent: {
          DEFAULT: "hsl(var(--accent))",
          foreground: "hsl(var(--accent-foreground))",
        },
        popover: {
          DEFAULT: "hsl(var(--popover))",
          foreground: "hsl(var(--popover-foreground))",
        },
        card: {
          DEFAULT: "hsl(var(--card))",
          foreground: "hsl(var(--card-foreground))",
        },
      },
      borderRadius: {
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)",
      },
      keyframes: {
        "accordion-down": {
          from: { height: "0" },
          to: { height: "var(--radix-accordion-content-height)" },
        },
        "accordion-up": {
          from: { height: "var(--radix-accordion-content-height)" },
          to: { height: "0" },
        },
      },
      animation: {
        "accordion-down": "accordion-down 0.2s ease-out",
        "accordion-up": "accordion-up 0.2s ease-out",
      },
    },
  },
  plugins: [require("tailwindcss-animate")],
} satisfies Config

export default config
```

### app/error.tsx

```typescript
'use client';

export default function Error () {
    return <div className={'absolute inset-0 grid place-content-center'}>
        <div className={'text-center'}>
            <h1 className={'text-white'}>An unexpected error occurred</h1>
            <p className={'text-gray-500'}>Please try again later</p>
        </div>
    </div>
}
```

### utils/getHumeAccessToken.ts

```typescript
import 'server-only';

import { fetchAccessToken } from "hume";

export const getHumeAccessToken = async () => {
  const accessToken = await fetchAccessToken({
    apiKey: String(process.env.HUME_API_KEY),
    secretKey: String(process.env.HUME_SECRET_KEY),
  });

  if (accessToken === "undefined") {
    return null;
  }

  return accessToken ?? null;
};

```

### public/style.css

```css
body {
  font-family: Arial, sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f9f9f9;
}

nav {
  background-color: #333;
  color: white;
}

nav ul {
  list-style-type: none;
  padding: 10px;
  display: flex;
  justify-content: center;
}

nav ul li {
  margin: 0 15px;
}

nav ul li a {
  color: white;
  text-decoration: none;
}

.container {
  text-align: center;
  padding: 50px;
}

h1 {
  color: #007BFF;
}

button {
  padding: 15px 30px;
  font-size: 16px;
  color: white;
  background-color: #007BFF;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

button:hover {
  background-color: #0056b3;
}

.hover-effect {
  transition: transform 0.3s, box-shadow 0.3s;
}

.hover-effect:hover {
  transform: scale(1.1);
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}

```

[18 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]