# Project export: Mirror

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: TreeHacks 2025
- Tagline: Reflecting on your past work, relationships, and learning has never been easier. Mirror turns your minute-to-minute into a comprehensive and indexable knowledge base in the form of a journal.
- Devpost: https://devpost.com/software/mirror-lmd54a
- GitHub: https://github.com/kevinzhu12/treehacks-glasses
- Demo: https://github.com/sarahjsu/tree_augmentos
- Video: https://www.youtube.com/embed/3FzmbTFD_9I?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Mistral: Best Use of Mistral AI API (AirPods x4 + $1,000 Cash + $1,000 API Credits))
- Team: 4 GitHub contributor(s) — clairebookworm (15 commits), Sarah (14 commits), Sanjith Udupa (7 commits), kevinzhu12 (4 commits)

## Devpost submission (written by the team)

### Inspiration

Remembering things is hard – and staying on top of your self improvement and reflection while juggling a busy schedule is even harder. As a team of busy students, one of whom recently started working a full time job, we decided to build Mirror as a tool to leverage unique smart glasses technology to revolutionize the way people handle self-improvement and reflection.

### What it does

Mirror is an AI-powered app compatible on all AugmentOS-enabled smart glasses which transcribes your conversations and generates actionable insights and summaries tuned for reflection in real time. Our processing agents will automatically update on the Mirror web interface as you speak, intelligently picking out the most appropriate points to give a well-rounded overview of your day, extracting todos, a general snapshot of your day, and important reflection questions. Just as important as understanding the specific details of your day is being able to look back in time at past memories and see how they fit into the context of the rest of your memories. Mirror offers full-text semantic search through all memory content to allow for quick and easy retrieval and recall of specific details even if you only have a small idea of what you’re looking for. Furthermore, visualizing the relationships between memories is made easy and beautiful via Mirror’s 3-Dimensional memory map which displays latent connections between memories. Each edge and connected component tells a story and provides insights into the relationships between your experiences. The best part – all of this is generated via our processing pipeline in real time as you talk! Throughout the entire day, Mirror will always be up to date with your latest memory insights.

### How we built it

Mirror is built in 3 parts: The AugmentOS Smart Glasses Client is an Android application written in Java which handles the interface with the smart glasses – handling both the voice transcription and relaying text back to the user via the display. Shoutout to the Mentra team for providing mentorship and lending us the Even Realities G1 Glasses that we built Mirror to work with. The backend is written in TypeScript via NextJS and handles interactions with both the smart glasses client and the web interface. Using ElasticSearch, Mistral AI, Vercel’s NextJS, and Codeium’s Windsurf, we created an intuitive interface to interact with your thoughts throughout the day.

### What we learned

We learned that working with experimental hardware is hard – and persistence is key. We are the R&D for the software for these glasses, testing the limits of what’s possible with them and trying to push the field forward.

### What's next

We would love to continue expanding Mirror’s interaction with our users, especially in terms of active reflection throughout the day. We envision a world where our agents can prompt you during conversations about related past conversations, remind you of your past reflections, and help guide you towards a more productive, social future.

## README (from the GitHub repository)

## Mirror 

> Reflecting on your past work, relationships, and learning has never been easier. Mirror turns your minute-to-minute into a comprehensive and indexable knowledge base in the form of a journal.

Mirror is an AI-powered app compatible on all AugmentOS-enabled smart glasses which transcribes your conversations and generates actionable insights and summaries tuned for reflection in real time. Our processing agents will automatically update on the Mirror web interface as you speak, intelligently picking out the most appropriate points to give a well-rounded overview of your day, extracting todos, a general snapshot of your day, and important reflection questions.

*by: Sarah Su, Claire Wang, Kevin Zhu, Sanjith Udupa*

![Cover photo](https://github.com/kevinzhu12/treehacks-glasses/blob/main/public/readme/mirror_cover.png)

Mirror is built in 3 parts:

*Taken from our [devpost](https://devpost.com/software/mirror-lmd54a?_gl=1*xzq0j*_gcl_au*MTE0ODY3OTU5MC4xNzM5NzY1MDQ3*_ga*MTUxMzc3MjMyMC4xNzM5NzY1MDQ4*_ga_0YHJK3Y10M*MTczOTc2NTA0Ny4xLjEuMTczOTc2NTA4Mi4wLjAuMA)*

The AugmentOS Smart Glasses Client is an Android application written in Java which handles the interface with the smart glasses – handling both the voice transcription and relaying text back to the user via the display. Shoutout to the [Mentra](https://mentra.glass/) team for providing mentorship and lending us the [Even Realities G1](https://www.evenrealities.com/g1) Glasses that we built Mirror to work with.

The backend is written in TypeScript via NextJS and handles interactions with both the smart glasses client and the web interface. Using ElasticSearch, Mistral AI, Vercel’s NextJS, and Codeium’s Windsurf, we created an intuitive interface to interact with your thoughts throughout the day.

Furthermore, visualizing the relationships between memories is made easy and beautiful via Mirror’s 3-Dimensional memory map which displays latent connections between memories. Each edge and connected component tells a story and provides insights into the relationships between your experiences.

The best part – all of this is generated via our processing pipeline in real time as you talk! Throughout the entire day, Mirror will always be up to date with your latest memory insights.


![Cover photo](https://github.com/kevinzhu12/treehacks-glasses/blob/main/public/readme/notes_sc.png)

**Watch our video:**

[![Video](https://img.youtube.com/vi/3FzmbTFD_9I/maxresdefault.jpg)](https://www.youtube.com/watch?v=3FzmbTFD_9I)


Link to [Android app repo](https://github.com/sarahjsu/tree_augmentos).

You can run the app yourself by using Android Studio (and combining the AugmentOS/Core app on an android phone with any smart glasses that has a mic) and sending a Pinggy tunnel to our web server. 


## Detected evidence (automated analysis)

Indexed codebase: 31 recognized source files, 54 KB.
- CSS (language) — detected in the code
- Next.js (technology) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Java (language) — claimed on Devpost, not found in the code
- Mistral AI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (40 of 40)

```
.gitignore
app/api/graph/route.ts
app/api/hello/route.ts
app/api/notes/all/route.ts
app/api/notes/find/route.ts
app/api/notes/load/route.ts
app/api/search/route.ts
app/api/transcript/route.ts
app/components/Menu.tsx
app/components/Notes.tsx
app/components/Search.tsx
app/components/Sidebar.tsx
app/components/ThemeToggle.tsx
app/globals.css
app/graph/layout.tsx
app/graph/page.tsx
app/layout.tsx
app/notes/[id]/page.tsx
app/notes/layout.tsx
app/notes/page.tsx
app/notes/testing.tsx
app/page.tsx
app/user/page.tsx
data/connections_old.json
data/connections.json
data/notes.json
data/transcripts.json
eslint.config.mjs
lib/config.ts
lib/elastic.ts
lib/graph_generator.ts
lib/notetaker.ts
lib/requests.ts
lib/storage.ts
next.config.ts
package.json
postcss.config.mjs
README.md
tailwind.config.ts
tsconfig.json
```

### Dependencies

- package.json: @elastic/elasticsearch@^8.17.0, @eslint/eslintrc@^3, @mistralai/mistralai@^1.5.0, @types/node@^20, @types/node-fetch@^2.6.12, @types/react@^19, @types/react-dom@^19, aframe@^1.6.0, dotenv@^16.4.7, eslint@^9, eslint-config-next@15.1.7, fetch@^1.1.0, fs@^0.0.1-security, next@15.1.7, openai@^4.85.1, postcss@^8, react@^19.0.0, react-dom@^19.0.0, react-force-graph@^1.47.3, react-force-graph-3d@^1.26.0, react-markdown@^9.0.3, remark-breaks@^4.0.0, tailwindcss@^3.4.1, typescript@^5

### Recent commits (newest first)

- Final readme commit
- Update README.md
- readme update
- click on  notes in graph to go to
- Merge branch 'main' of https://github.com/kevinzhu12/treehacks-glasses
- zoom
- new commmit
- ui change
- added dark/light icon
- small ui change
- center
- fint font edits
- font edits
- changed fonts lol
- goat neel
- remove dates from titles
- stupid data
- just reupdating data
- i hate ur guts claire poo poo face
- hello

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

### package.json

```
{
  "name": "treehacks-project",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@elastic/elasticsearch": "^8.17.0",
    "@mistralai/mistralai": "^1.5.0",
    "aframe": "^1.6.0",
    "dotenv": "^16.4.7",
    "fetch": "^1.1.0",
    "fs": "^0.0.1-security",
    "next": "15.1.7",
    "openai": "^4.85.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-force-graph": "^1.47.3",
    "react-force-graph-3d": "^1.26.0",
    "react-markdown": "^9.0.3",
    "remark-breaks": "^4.0.0"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@types/node": "^20",
    "@types/node-fetch": "^2.6.12",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.1.7",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### app/page.tsx

```typescript
"use client";

import { redirect } from "next/navigation";
import React, { useState } from "react";
// import { sendOpenAIRequest } from "../lib/requests";
// import Notes from "../components/Notes";

export default function Home() {
  redirect("/notes");
  // return (
  //   <main className="min-h-screen bg-[#faf9f6]">
  //     <div className="max-w-3xl mx-auto px-8 py-12">
  //       <div className="text-center">
  //         <h1 className="text-4xl font-bold mb-6 text-gray-800">Welcome</h1>
  //         <div className="text-gray-500 mb-8">
  //           Click "New Note" in the sidebar to start writing
  //         </div>
  //       </div>
  //     </div>
  //   </main>
  // );
}

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import Menu from "./components/Menu";

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={`${geistSans.variable} ${geistMono.variable} antialiased dark:bg-gray-900 dark:text-white transition-colors`}>
        <Menu />
        {children}
      </body>
    </html>
  );
}

```

### app/user/page.tsx

```typescript
"use client;"


```

### app/graph/layout.tsx

```typescript
"use client";

import Sidebar from "../components/Sidebar";
import { useState, useEffect } from "react";
import { NoteContent } from "./[id]/page";

export default function NotesLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="flex">
      <div className="ml-64 flex-1">{children}</div>
    </div>
  );
}

```

### app/notes/layout.tsx

```typescript
"use client";

import Sidebar from "../components/Sidebar";
import { useState, useEffect } from "react";
import { NoteContent } from "./[id]/page";

export default function NotesLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const [notes, setNotes] = useState<Record<string, NoteContent>>({});

  useEffect(() => {
    async function fetchNotes() {
      const response = await fetch("/api/notes/all");
      const savedNotes = await response.json();
      console.log("savedNotes", savedNotes);
      setNotes(savedNotes);
    }

    fetchNotes();
  }, []);


  return (
    <div className="flex">
      <Sidebar notes={notes} />
      <div className="ml-64 flex-1">{children}</div>
    </div>
  );
}

```

### app/notes/page.tsx

```typescript
"use client";

import Graph from "../graph/page"

export default function NotesHome() {
  return (
    <main className="min-h-screen bg-[#faf9f6] flex items-center justify-center">
      <div className="text-center max-w-md px-8">
      <div style={{zIndex: 1}} className="graph-display">
          <Graph />
        </div>
        <div className="left-0 right-0 text-center" style={{zIndex: 0, fontFamily : "DM Sans, Helvetica", userSelect: "none"}}>
          <h1 style={{fontWeight: "bold", fontFamily: "DM Sans, Helvetica", fontSize: "3rem"}} className="text-3xl font-semibold text-gray-800 mb-3">
            Mirror
          </h1>
          <p className="text-base text-gray-400 mb-12">
            Capture your thoughts and reflections. Select an entry to begin.
          </p>
        </div>
        

        {/* Subtle arrow pointing to sidebar */}
        <div className="absolute left-24 top-1/2 transform -translate-y-1/2 text-gray-300">
          <svg
            className="w-6 h-6"
            fill="none"
            stroke="currentColor"
            viewBox="0 0 24 24"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={1.5}
              d="M10 19l-7-7m0 0l7-7m-7 7h18"
            />
          </svg>
        </div>
      </div>
    </main>
  );
}

```

### app/graph/page.tsx

```typescript
"use client";
import React, { useEffect, useState } from "react";
import dynamic from "next/dynamic";
import "aframe";
import { useRouter } from "next/navigation";

import { buildNote } from "@/lib/notetaker";

const ForceGraph3D = dynamic(
  () => import("react-force-graph").then((mod) => mod.ForceGraph3D),
  { ssr: false }
);

const Graph = () => {
  const router = useRouter();
  const [myData, setMyData] = useState<any>();
  const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
  const [selectedNote, setSelectedNote] = useState<string | null>(null);
  const [showButton, setShowButton] = useState(false);

  const graphRef = React.useRef<any>(null);

  useEffect(() => {
    const getGraph = async () => {
      const res = await fetch("/api/graph");
      const data = await res.json();
      setMyData(data.graph);
    };
    getGraph();

    const updateDimensions = () => {
      setDimensions({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    };

    updateDimensions();
    window.addEventListener("resize", updateDimensions);
    return () => window.removeEventListener("resize", updateDimensions);
  }, []);

  if (!myData) return null;

  return (
    <div className="absolute inset-0 z-0 w-full h-full text-center">
      <ForceGraph3D
        ref={graphRef}
        graphData={myData}
        nodeLabel="id"
        nodeAutoColorBy="group"
        nodeResolution={100}
        linkDirectionalParticles={2}
        linkDirectionalParticleResolution={12}
        backgroundColor={"rgba(0,0,0,0)"}
        onNodeClick={async (node: any) => {
          // Aim at node from outside it
          const distance = 75;
          const distRatio = 1 + distance / Math.hypot(node.x, node.y, node.z);

          const newPos = node.x || node.y || node.z
            ? { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }
            : { x: 0, y: 0, z: distance }; // special case if node is in (0,0,0)

          graphRef.current.cameraPosition(
            newPos, // new position
            node, // lookAt ({ x, y, z })
            2500 // ms transition duration
          );

          // Find the note date
          const res = await fetch("/api/notes/find?name=" + node.id);
          const date = await res.json();

          setSelectedNote(date === "undefined" ? null : date);
          setShowButton(false); // Reset button visibility

          // Show button after delay
          setTimeout(() => {
            setShowButton(true);
          }, 2500); // Match the camera transition duration
        }}
        linkColor={() =>
          document.documentElement.classList.contains("dark")
            ? "#ffffff"
            : "#000000"
        }
        linkVisibility={true}
        linkOpacity={0.8}
        width={dimensions.width}
        height={dimensions.height}
      />
      {selectedNote && showButton && (
        <div 
          className="fixed bottom-8 left-1/2 transform -translate-x-1/2 z-50"
          style={{ fontFamily: "DM Sans, Helvetica" }}
        >
          <button
            onClick={() => router.push(`/notes/${selectedNote}`)}
            className="px-6 py-3 bg-[#faf9f6] dark:bg-gray-800 hover:bg-gray-100 dark:hover:bg-gray-700 
                     text-gray-800 dark:text-white rounded-lg text-sm font-medium transition-colors
                     shadow-lg hover:shadow-xl border border-gray-200 dark:border-gray-700"
          >
            Open Note
          </button>
        </div>
      )}
    </div>
  );
};

export default Graph;

```

### app/api/hello/route.ts

```typescript
import { NextResponse } from "next/server";

export async function GET() {
  return NextResponse.json({
    message: "Hello, Next.js!",
  });
}

export async function POST(request: Request) {
  const body = await request.json();
  
  return NextResponse.json({
    message: 'Received POST request',
    data: body,
    timestamp: new Date().toISOString(),
  });
}
```

### app/api/graph/route.ts

```typescript
import { NextResponse } from "next/server";
import { getGraph } from "@/lib/storage";

export async function GET() {
  const graph = getGraph();

  return NextResponse.json({ graph });
}

export async function POST(request: Request) {
  const body = await request.json();

  return NextResponse.json({
    message: "Received POST request",
    data: body,
    timestamp: new Date().toISOString(),
  });
}

```

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