# Project export: Focusify

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: UC Berkeley AI Hackathon 2026
- Tagline: Focusify turns your day into clarity. Log time by voice, text, or auto-tracking, and AI sorts it into a clean hour-by-hour timeline; so you can focus on what actually matters.
- Devpost: https://devpost.com/software/focusify-82zrno
- GitHub: https://github.com/Samar1006/time-tracker.git
- Video: https://www.youtube.com/embed/hbXrH9ztprw?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 5 GitHub contributor(s) — Cursor (53 commits), Samar1006 (30 commits), Devarsh Jani (9 commits), Nyein Nyein Aye (5 commits), Claude Opus 4.8 (1 commits)

## Devpost submission (written by the team)

### Overview

Know where your time goes. Speak it or type it — AI turns your day into a clean, categorized timeline.

### Inspiration

Time is the one resource you can't get back, yet most of us have no real sense of where it goes. We'd finish a day "feeling busy" but unable to say what we actually did with it. Seeing where your time goes is the first step to taking it back — but every tracker we tried made logging the chore: Clockify leans on manual timers (wasting the time you're trying to save), Timely doesn't track on your phone, and Timing is slow on iOS. So we flipped the problem: instead of making people fight a stopwatch, what if you could just say what you did and let an AI agent organize it? Capture as much of your time as possible, any way possible — voice, text, or (eventually) automatic background monitoring.

### What it does

Capture by voice or text — say "from 9 to 11 I worked on the API, then took a break," or type it. AI parsing — Claude turns messy natural language into structured time blocks with real start/end times. Automatic categorization — every activity is sorted into Work, Communication, Learning, Entertainment, or Break — no manual tagging. Hour-by-hour visualization — a clean, color-coded timeline, a weekly History view, and an Apps & Websites breakdown. Accounts, light/dark mode, and a polished branded UI.

### How we built it

A full-stack app split across four focused tracks, integrated through a shared API contract: Speech-to-text: Deepgram transcribes recorded audio. AI agent: the Anthropic (Claude) API converts a transcript into categorized, time-stamped blocks — with a fast offline heuristic parser as a fallback. Frontend: Angular — dashboard, timeline, history, settings, dark mode. Backend: Node.js + Express, with Redis for storage (graceful in-memory fallback) and JWT auth. and the day's total tracked time is simply $T = \sum_h d_h$ — the number rendered at the top of your dashboard.

### Challenges we ran into

Limited time — scoping an ambitious "track time any way possible" vision down to a working MVP. Privacy & permissions — native background monitoring on Mac/iOS needs heavy OS entitlements, so we shipped the AI capture methods first. Natural-language time parsing — "an hour," "until 9 pm," overnight spans, and shared AM/PM across ranges were all deceptively hard. Integration friction — merging four parallel branches meant rebases and conflicts (including one half-merged feature that briefly broke main); a written API contract and smaller PRs kept us unblocked.

### What we learned

AI is a feature, not the whole product. The magic lived in the glue — good prompts, structured outputs, and graceful fallbacks — not the model alone. Contracts beat coordination. A shared API contract saved more time than constant syncing. Integrate early and often. Small, frequent PRs hurt far less than one giant end-of-hackathon merge. Design carries the demo. A consistent theme and thoughtful UX made the same features feel dramatically more compelling.

### What's next

Background monitoring of Mac & iOS usage — auto-capture apps and sites, no logging required. Integrations — calendar, Strava, Apple Health. Import a schedule from an image as another capture method. App blocking during focus hours. Personalized AI categories that learn what "productive" means for you.

## README (from the GitHub repository)

# time-tracker

Time tracker with automatic browser/Mac monitoring, voice logging, and an Apple Calendar–style day view.

## Features

- **Dashboard** — vertical day timeline, date navigation, pinch-to-zoom, scroll preservation
- **Voice logging** — mic → transcribe (Deepgram) → parse → timeline blocks; live transcript while recording
- **Voice mutations** — natural language edits (move/shift/shorten/cancel blocks) via voice commands
- **Chrome extension** — background tab/domain tracking → timeline
- **Mac menu bar app** — frontmost app focus tracking ([`clients/mac/`](clients/mac/))
- **Stats page** (`/stats`) — day/week/month breakdown
- **Category settings** (`/settings`) — customize AI categorization hints for voice parse
- **Auth** — email/password + Google sign-in; JWT on all activity routes

## Quick start

```bash
# Terminal 1 — backend
cd server && npm install && cp .env.example .env && npm run dev
```

```bash
# Terminal 2 — frontend
cd frontend/time-tracker-app && npm install && npm start
```

Open **http://localhost:4200** (mic requires `localhost`, not a LAN IP).

**Demo login:** `demo@timetracker.test` / `Demo1234!`

**Optional — Chrome extension:** load unpacked from [`browser-extension/`](browser-extension/) ([install guide](browser-extension/README.md)).

## Project layout

```
time-tracker/
├── server/                      Express API, Redis/memory store, AI parse
├── frontend/time-tracker-app/   Angular dashboard
├── browser-extension/           Chrome MV3 background monitor
├── clients/mac/                 macOS menu bar tracker
├── API_CONTRACT.md              API reference
└── DECISIONS.md                 Architecture notes
```

## Environment variables

Full list in [`server/.env.example`](server/.env.example). Summary:

| Variable | Required? | Purpose |
|----------|-----------|---------|
| `DEEPGRAM_API_KEY` | For voice STT | Speech-to-text |
| `ANTHROPIC_API_KEY` | Optional | LLM schedule refine |
| `JWT_SECRET` | Optional locally | Auth tokens |
| `REDIS_URL` | Optional | Persistence (memory fallback OK for demo) |
| `GOOGLE_CLIENT_ID` | For Google login | OAuth web client ID |

## Testing

```bash
cd server && npm test          # 97 tests, no keys required
cd frontend/time-tracker-app && npm run build
```

## Demo script

1. **Login** → dashboard shows vertical timeline
2. **Browse** github.com in Chrome → switch tab → block appears
3. **Log with voice** → “From 9 to 10 AM coding” → block at 9 AM
4. Visit **`/stats`** and **`/settings`**

## Team / components

| Area | Folder |
|------|--------|
| Backend API | `server/` |
| Angular UI | `frontend/time-tracker-app/` |
| Extension | `browser-extension/` |
| Mac client | `clients/mac/` |

## Links

- **API:** [`API_CONTRACT.md`](API_CONTRACT.md)
- **Server setup:** [`server/README.md`](server/README.md)
- **Extension:** [`browser-extension/README.md`](browser-extension/README.md)
- **Mac app:** [`clients/mac/README.md`](clients/mac/README.md)
- **Client event contract:** [`clients/INTEGRATION.md`](clients/INTEGRATION.md)
- **Agent / architecture:** [`AGENTS.md`](AGENTS.md), [`DECISIONS.md`](DECISIONS.md)


## Detected evidence (automated analysis)

Indexed codebase: 125 recognized source files, 584 KB.
- Angular (technology) — detected in the code
- Anthropic (technology) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Redis (technology) — detected in the code
- Swift (language) — detected in the code
- TypeScript (language) — detected in the code
- CSS (language) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: commit authorship or trailers
- AI coding agent: Codex — evidence: config files committed to the repository
- AI coding agent: Cursor — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (120 of 172)

```
.gitignore
AGENTS.md
API_CONTRACT.md
backend/.gitkeep
browser-extension/background.js
browser-extension/content-bridge.js
browser-extension/manifest.json
browser-extension/options.html
browser-extension/options.js
browser-extension/popup.html
browser-extension/popup.js
browser-extension/README.md
clients/INTEGRATION.md
clients/mac/README.md
clients/mac/TimeTrackerMac.xcodeproj/project.pbxproj
clients/mac/TimeTrackerMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
clients/mac/TimeTrackerMac.xcodeproj/xcshareddata/xcschemes/TimeTrackerMac.xcscheme
clients/mac/TimeTrackerMac/App/AppState.swift
clients/mac/TimeTrackerMac/App/MenuBarView.swift
clients/mac/TimeTrackerMac/Assets.xcassets/AppIcon.appiconset/Contents.json
clients/mac/TimeTrackerMac/Assets.xcassets/Contents.json
clients/mac/TimeTrackerMac/Auth/KeychainStore.swift
clients/mac/TimeTrackerMac/Auth/LoginView.swift
clients/mac/TimeTrackerMac/Info.plist
clients/mac/TimeTrackerMac/Models/ActivityEvent.swift
clients/mac/TimeTrackerMac/Network/APIClient.swift
clients/mac/TimeTrackerMac/Network/EventQueue.swift
clients/mac/TimeTrackerMac/TimeTrackerMacApp.swift
clients/mac/TimeTrackerMac/Tracking/AppFocusTracker.swift
clients/mac/TimeTrackerMac/Tracking/BrowserFilter.swift
clients/mac/TimeTrackerMac/Tracking/EventBuilder.swift
clients/mac/TimeTrackerMac/Tracking/FocusInterval.swift
clients/mac/TimeTrackerMac/Tracking/SystemAppFilter.swift
clients/mac/TimeTrackerMacTests/EventBuilderTests.swift
DECISIONS.md
frontend/.gitkeep
frontend/time-tracker-app/.editorconfig
frontend/time-tracker-app/.gitignore
frontend/time-tracker-app/.prettierrc
frontend/time-tracker-app/.vscode/extensions.json
frontend/time-tracker-app/.vscode/launch.json
frontend/time-tracker-app/.vscode/mcp.json
frontend/time-tracker-app/.vscode/tasks.json
frontend/time-tracker-app/angular.json
frontend/time-tracker-app/package.json
frontend/time-tracker-app/proxy.conf.json
frontend/time-tracker-app/README.md
frontend/time-tracker-app/src/app/app.config.ts
frontend/time-tracker-app/src/app/app.html
frontend/time-tracker-app/src/app/app.routes.ts
frontend/time-tracker-app/src/app/app.scss
frontend/time-tracker-app/src/app/app.spec.ts
frontend/time-tracker-app/src/app/app.ts
frontend/time-tracker-app/src/app/core/constants/categories.ts
frontend/time-tracker-app/src/app/core/guards/auth.guard.ts
frontend/time-tracker-app/src/app/core/interceptors/auth.interceptor.ts
frontend/time-tracker-app/src/app/core/models/auth.model.ts
frontend/time-tracker-app/src/app/core/models/monitor.model.ts
frontend/time-tracker-app/src/app/core/models/timeline.model.ts
frontend/time-tracker-app/src/app/core/services/auth.service.ts
frontend/time-tracker-app/src/app/core/services/category-context.service.ts
frontend/time-tracker-app/src/app/core/services/monitor.service.ts
frontend/time-tracker-app/src/app/core/services/theme.service.ts
frontend/time-tracker-app/src/app/core/services/timeline-keyboard-settings.service.ts
frontend/time-tracker-app/src/app/core/services/timeline-undo.service.ts
frontend/time-tracker-app/src/app/core/services/timeline-viewport-storage.service.ts
frontend/time-tracker-app/src/app/core/services/timeline.service.ts
frontend/time-tracker-app/src/app/core/services/voice-log.service.ts
frontend/time-tracker-app/src/app/core/utils/duration.util.ts
frontend/time-tracker-app/src/app/core/utils/live-speech-recognition.util.ts
frontend/time-tracker-app/src/app/core/utils/voice-block.util.ts
frontend/time-tracker-app/src/app/features/auth/pages/login/login.component.html
frontend/time-tracker-app/src/app/features/auth/pages/login/login.component.scss
frontend/time-tracker-app/src/app/features/auth/pages/login/login.component.ts
frontend/time-tracker-app/src/app/features/auth/pages/signup/signup.component.html
frontend/time-tracker-app/src/app/features/auth/pages/signup/signup.component.scss
frontend/time-tracker-app/src/app/features/auth/pages/signup/signup.component.ts
frontend/time-tracker-app/src/app/features/auth/validators/password-match.validator.ts
frontend/time-tracker-app/src/app/features/dashboard/components/activity-calendar/activity-calendar.component.html
frontend/time-tracker-app/src/app/features/dashboard/components/activity-calendar/activity-calendar.component.scss
frontend/time-tracker-app/src/app/features/dashboard/components/activity-calendar/activity-calendar.component.ts
frontend/time-tracker-app/src/app/features/dashboard/components/dashboard-header/dashboard-header.component.html
frontend/time-tracker-app/src/app/features/dashboard/components/dashboard-header/dashboard-header.component.scss
frontend/time-tracker-app/src/app/features/dashboard/components/dashboard-header/dashboard-header.component.ts
frontend/time-tracker-app/src/app/features/dashboard/components/dashboard-sidebar/dashboard-sidebar.component.html
frontend/time-tracker-app/src/app/features/dashboard/components/dashboard-sidebar/dashboard-sidebar.component.scss
frontend/time-tracker-app/src/app/features/dashboard/components/dashboard-sidebar/dashboard-sidebar.component.ts
frontend/time-tracker-app/src/app/features/dashboard/components/stat-card/stat-card.component.html
frontend/time-tracker-app/src/app/features/dashboard/components/stat-card/stat-card.component.scss
frontend/time-tracker-app/src/app/features/dashboard/components/stat-card/stat-card.component.ts
frontend/time-tracker-app/src/app/features/dashboard/components/timeline-chart/timeline-chart.component.html
frontend/time-tracker-app/src/app/features/dashboard/components/timeline-chart/timeline-chart.component.scss
frontend/time-tracker-app/src/app/features/dashboard/components/timeline-chart/timeline-chart.component.ts
frontend/time-tracker-app/src/app/features/dashboard/components/timeline-chart/timeline-drag.util.ts
frontend/time-tracker-app/src/app/features/dashboard/pages/dashboard/dashboard.component.html
frontend/time-tracker-app/src/app/features/dashboard/pages/dashboard/dashboard.component.scss
frontend/time-tracker-app/src/app/features/dashboard/pages/dashboard/dashboard.component.ts
frontend/time-tracker-app/src/app/features/dashboard/utils/calendar.util.ts
frontend/time-tracker-app/src/app/features/dashboard/utils/dashboard-stats.util.ts
frontend/time-tracker-app/src/app/features/dashboard/utils/timeline-optimistic.util.ts
frontend/time-tracker-app/src/app/features/settings/pages/settings/settings.component.html
frontend/time-tracker-app/src/app/features/settings/pages/settings/settings.component.scss
frontend/time-tracker-app/src/app/features/settings/pages/settings/settings.component.ts
frontend/time-tracker-app/src/app/features/stats/components/activity-donut-chart/activity-donut-chart.component.html
frontend/time-tracker-app/src/app/features/stats/components/activity-donut-chart/activity-donut-chart.component.scss
frontend/time-tracker-app/src/app/features/stats/components/activity-donut-chart/activity-donut-chart.component.ts
frontend/time-tracker-app/src/app/features/stats/pages/stats/stats.component.html
frontend/time-tracker-app/src/app/features/stats/pages/stats/stats.component.scss
frontend/time-tracker-app/src/app/features/stats/pages/stats/stats.component.ts
frontend/time-tracker-app/src/app/shared/components/auth-form-header/auth-form-header.component.html
frontend/time-tracker-app/src/app/shared/components/auth-form-header/auth-form-header.component.scss
frontend/time-tracker-app/src/app/shared/components/auth-form-header/auth-form-header.component.ts
frontend/time-tracker-app/src/app/shared/components/day-date-nav/day-date-nav.component.html
frontend/time-tracker-app/src/app/shared/components/day-date-nav/day-date-nav.component.scss
frontend/time-tracker-app/src/app/shared/components/day-date-nav/day-date-nav.component.ts
frontend/time-tracker-app/src/app/shared/components/google-sign-in-button/google-sign-in-button.component.html
frontend/time-tracker-app/src/app/shared/components/google-sign-in-button/google-sign-in-button.component.scss
frontend/time-tracker-app/src/app/shared/components/google-sign-in-button/google-sign-in-button.component.ts
frontend/time-tracker-app/src/app/shared/components/password-field/password-field.component.html
frontend/time-tracker-app/src/app/shared/components/password-field/password-field.component.scss
[52 more files omitted for size]
```

### Dependencies

- frontend/time-tracker-app/package.json: @angular/build@^21.2.13, @angular/cli@^21.2.13, @angular/common@^21.2.0, @angular/compiler@^21.2.0, @angular/compiler-cli@^21.2.0, @angular/core@^21.2.0, @angular/forms@^21.2.0, @angular/platform-browser@^21.2.0, @angular/router@^21.2.0, jsdom@^28.0.0, prettier@^3.8.1, rxjs@~7.8.0, tslib@^2.3.0, typescript@~5.9.2, vitest@^4.0.8
- server/package.json: @anthropic-ai/sdk@^0.69.0, @deepgram/sdk@^3.9.0, bcryptjs@^2.4.3, cors@^2.8.5, dotenv@^16.4.5, express@^4.19.2, jsonwebtoken@^9.0.2, redis@^4.7.0

### Recent commits (newest first)

- Merge pull request #43 from Samar1006/feature/timeline-vim-motions
- Document mac-tracker browser ingest filtering in API contract.
- Timeline UX polish: viewport persistence, motions, and tracker dedupe.
- Follow cursor with tooltip and fix delete operator arming.
- Add vim visual selection, yank/paste, and delete operators on timeline.
- Merge pull request #42 from Samar1006/feature/timeline-insert-rename
- Add insert mode, cursor editing shortcuts, and rename with classification.
- Merge pull request #41 from Samar1006/feature/timeline-cursor-navigation
- Fix Ctrl-d/u half-page moves and j/k scroll flicker at margin.
- Add vim-style timeline cursor with zoom-aware j/k steps.
- Merge pull request #40 from Samar1006/feature/timeline-jk-count-prefix
- Classify manual events on creation.
- Merge pull request #39 from Samar1006/feature/timeline-jk-count-prefix
- Fix timeline flicker and preserve selection across edits.
- Improve multi-select, bulk delete undo, and group edge moves.
- Fix overlap columns being offset by non-overlapping events.
- Merge feature/timeline-keyboard-zoom into gg block-edge branch.
- Move selected blocks to day edges with gg and G.
- Add - and + keyboard shortcuts for timeline zoom.
- Fix timeline overlap columns and improve create/zoom UX.

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

### AGENTS.md

```markdown
# Agent instructions — time-tracker hackathon

**Before doing any work, read these files in order:**

1. [`TASKS.md`](TASKS.md) — your assigned tasks, branch names, what not to touch
2. [`API_CONTRACT.md`](API_CONTRACT.md) — API routes, request/response shapes, env vars
3. [`DECISIONS.md`](DECISIONS.md) — architecture decisions (don't reverse silently)

## Branch rules

- **`main`** is the integration branch. All work starts with `git checkout main && git pull`.
- **Do not use merged branch names** (`ai-pipeline`, `dj`, `ingestion-timeline`) for new work.
- Use the branch names in `TASKS.md` for your role (`ai-followups`, `frontend-integration`).

## Role detection

| If you're working on… | Read section in TASKS.md | Primary folder |
|----------------------|--------------------------|----------------|
| AI / voice / categorization | Allisonmini — AI follow-ups | `server/src/services/`, `server/src/routes/schedule.js` |
| Frontend / Angular / timeline UI | Frontend team — integration | `frontend/time-tracker-app/` |
| Ingestion / auth / Redis / timeline API | Already on `main` — only fix bugs if asked | `server/src/routes/activity.js`, `server/src/routes/auth.js` |

## Hard rules

- Do not change API routes or schemas without updating `API_CONTRACT.md`.
- Do not edit files outside your ownership section in `TASKS.md`.
- Do not introduce new dependencies without stating why in the PR.
- Prefer small PRs to `main`.

## Foreman vs PR review

- **Foreman agent** — branch coordination, keeps docs current (does not review/merge PRs).
- **PR review agent** — reviews and merges PRs.

When in doubt, add a question to `TASKS.md` instead of guessing.

```

### TASKS.md

```markdown
# Tasks

> **All agents:** Read this file and [`API_CONTRACT.md`](API_CONTRACT.md) before starting work. Update your task status when you finish.

**Last foreman check:** 2026-06-21 — PR #5 merged; `dj` = frontend integration branch

---

## Team delegation (current sprint)

### Frontend team — **`dj` branch** (frontend integration)

> **`dj` is the active frontend branch** — not stale. Rebase onto `main` before opening PR.

**Branch:** `git checkout dj && git pull origin dj && git rebase origin/main`

| Priority | Task | Status |
|----------|------|--------|
| P0 | `AuthService` + JWT storage | 🟡 On `dj` (`core/services/auth.service.ts`) |
| P0 | Dev proxy `/api` → `:4000` | 🟡 On `dj` (`proxy.conf.json`) |
| P0 | Wire login + signup | 🟡 On `dj` |
| P1 | Dashboard + timeline chart | 🟡 On `dj` (`features/dashboard/`) |
| P1 | Auth guard + post-login routing | 🟡 On `dj` (`core/guards/auth.guard.ts`) |

**Before PR:** rebase onto `main` (picks up PR #5 categorizer swap). **Remove `.tmp-gh/`** from branch — accidental gh CLI binary committed.

**Demo credentials:** `demo@timetracker.test` / `Demo1234!` → user ID `user-demo-1`

---

### Allisonmini — AI follow-ups

> **Delegated by PR review agent (2026-06-21):** PR #6 reviewed — **REQUEST CHANGES**. Rebase required before merge. Do not use `ai-pipeline` (merged/deleted); work on **`voice-demo`** only.

| Priority | Task | Status |
|----------|------|--------|
| P0 | `categoryHint` → `categorizationService` | ✅ Merged ([#5](https://github.com/Samar1006/time-tracker/pull/5)) |
| P1 | Voice demo page `/demo` | 🔴 PR [#6](https://github.com/Samar1006/time-tracker/pull/6) — **rebase + fix `app.js` conflict** |

**Branch:** `voice-demo` for PR #6 only.

**Your action items (PR #6):**

1. `git checkout voice-demo && git fetch origin && git rebase origin/main`
2. Resolve `server/src/app.js` — **keep both**:
   - `app.use('/api/auth', authRouter)` (from `main`)
   - `app.use('/demo', express.static(...))` (from your commit)
3. `cd server && npm test` — must pass **30/30** (includes auth tests)
4. `git push --force-with-lease origin voice-demo`
5. Comment on PR #6 when ready; PR review agent will merge

**Do not touch:** `frontend/time-tracker-app/` (Frontend team / `dj` branch).

---

### Samar / foreman

- Keep docs current; track branch health
- PR review agent merges PRs

---

## Branch status

| Branch | vs `main` | PR | Action |
|--------|-----------|-----|--------|
| `main` | — | — | 30 tests passing; categorizer swap merged |
| `dj` | **+1 / −2** | none open yet | Frontend integration — **rebase then PR** |
| `voice-demo` | +1 / −8 | [#6](https://github.com/Samar1006/time-tracker/pull/6) open | **Allisonmini:** rebase + `app.js` conflict (see above) |
| `categorizer-swap` | merged | [#5](https://github.com/Samar1006/time-tracker/pull/5) ✅ | Delete remote branch |
| `auth-backend`, `ingestion-timeline`, `ai-pipeline` | merged | — | Safe to delete |

---

## MVP progress

| # | Task | Status |
|---|
[truncated — 878 more characters]
```

### server/package.json

```
{
  "name": "time-tracker-server",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "description": "Time-tracker backend — activity ingestion + AI & voice pipeline (Deepgram + Claude schedule parsing + categorization)",
  "main": "src/server.js",
  "scripts": {
    "start": "node src/server.js",
    "dev": "node --watch src/server.js",
    "test": "node --test test/**/*.test.js test/helpers/http.js",
    "redis:test": "node scripts/redis-test.mjs"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.69.0",
    "@deepgram/sdk": "^3.9.0",
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "dotenv": "^16.4.5",
    "express": "^4.19.2",
    "jsonwebtoken": "^9.0.2"
  },
  "optionalDependencies": {
    "redis": "^4.7.0"
  }
}

```

### frontend/time-tracker-app/package.json

```
{
  "name": "time-tracker-app",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test"
  },
  "private": true,
  "packageManager": "npm@11.13.0",
  "dependencies": {
    "@angular/common": "^21.2.0",
    "@angular/compiler": "^21.2.0",
    "@angular/core": "^21.2.0",
    "@angular/forms": "^21.2.0",
    "@angular/platform-browser": "^21.2.0",
    "@angular/router": "^21.2.0",
    "rxjs": "~7.8.0",
    "tslib": "^2.3.0"
  },
  "devDependencies": {
    "@angular/build": "^21.2.13",
    "@angular/cli": "^21.2.13",
    "@angular/compiler-cli": "^21.2.0",
    "jsdom": "^28.0.0",
    "prettier": "^3.8.1",
    "typescript": "~5.9.2",
    "vitest": "^4.0.8"
  }
}
```

### server/src/server.js

```javascript
// server.js — process entry point.
import 'dotenv/config';
import { createApp } from './app.js';
import { warmUpRedis } from './services/redisClient.js';

const port = Number(process.env.PORT) || 4000;
createApp().listen(port, () => {
  console.log(`time-tracker server listening on http://localhost:${port}`);
  void warmUpRedis();
});

```

### server/src/app.js

```javascript
// app.js — Express app wiring. Kept separate from server.js so tests can
// import the app without binding a port.
import express from 'express';
import cors from 'cors';
import path from 'path';
import { fileURLToPath } from 'url';
import activityRouter from './routes/activity.js';
import authRouter from './routes/auth.js';
import scheduleRouter from './routes/schedule.js';
import { transcribeUrl, transcribeBuffer } from './services/deepgramService.js';
import { categorizeDomain, categorizeActivity } from './services/categorizationService.js';

export function createApp() {
  const app = express();
  app.use(cors());
  app.use(express.json({ limit: '2mb' }));

  app.get('/health', (_req, res) => res.json({ ok: true }));

  // Static demo page for the AI pipeline — open http://localhost:4000/demo
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
  app.use('/demo', express.static(path.join(__dirname, '..', 'public')));

  // Auth — login, signup, JWT sessions.
  app.use('/api/auth', authRouter);

  // Samar — activity ingestion, storage, hour-by-hour timeline.
  app.use('/api', activityRouter);

  // Allison — AI & voice pipeline.
  app.use('/api/schedule', scheduleRouter);

  // Speech-to-text. Accepts a hosted audio URL { url } or base64 audio
  // { audioBase64 }. Requires DEEPGRAM_API_KEY.
  app.post('/api/transcribe', async (req, res) => {
    const { url, audioBase64 } = req.body ?? {};
    if (!url && !audioBase64) {
      return res.status(400).json({ error: 'Provide "url" or "audioBase64".' });
    }
    try {
      const result = url
        ? await transcribeUrl(url)
        : await transcribeBuffer(Buffer.from(audioBase64, 'base64'));
      res.json({ transcript: result.transcript, words: result.words });
    } catch (err) {
      res.status(502).json({ error: err.message });
    }
  });

  // Categorization (used by frontend/demo and by Samar's aggregation).
  app.post('/api/categorize/domain', (req, res) => {
    const { domain } = req.body ?? {};
    if (!domain) return res.status(400).json({ error: 'Provide "domain".' });
    res.json(categorizeDomain(domain));
  });

  app.post('/api/categorize/activity', async (req, res) => {
    const { text, useVector } = req.body ?? {};
    if (!text) return res.status(400).json({ error: 'Provide "text".' });
    res.json(await categorizeActivity(text, { useVector: !!useVector }));
  });

  return app;
}

export default createApp;

```

### frontend/time-tracker-app/src/main.ts

```typescript
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { App } from './app/app';

bootstrapApplication(App, appConfig)
  .catch((err) => console.error(err));

```

### frontend/time-tracker-app/src/app/app.ts

```typescript
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  templateUrl: './app.html',
  styleUrl: './app.scss'
})
export class App {}

```

### browser-extension/popup.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>Time Tracker Monitor</title>
    <style>
      body {
        font-family: system-ui, sans-serif;
        width: 280px;
        margin: 0;
        padding: 1rem;
        color: #1e1b4b;
      }
      h1 {
        font-size: 1rem;
        margin: 0 0 0.75rem;
      }
      .status {
        font-size: 0.875rem;
        color: #6b7280;
      }
      .status--on {
        color: #059669;
      }
      a {
        display: inline-block;
        margin-top: 0.75rem;
        color: #6366f1;
        font-size: 0.875rem;
      }
    </style>
  </head>
  <body>
    <h1>Background Monitor</h1>
    <p id="status" class="status">Loading…</p>
    <p id="domain" class="status"></p>
    <p id="queue" class="status"></p>
    <a href="#" id="options">Extension settings</a>
    <script src="popup.js"></script>
  </body>
</html>

```

### browser-extension/content-bridge.js

```javascript
const USER_ID_ATTR = 'data-tracker-user-id';
const EXTENSION_ATTR = 'data-tracker-extension';
const TOKEN_KEY = 'authToken';
const DASHBOARD_TOKEN_KEY = 'time-tracker-token';

function markExtensionReady() {
  document.documentElement.setAttribute(EXTENSION_ATTR, 'ready');
}

function syncSessionFromPage() {
  const token = sessionStorage.getItem(DASHBOARD_TOKEN_KEY)?.trim();
  if (token) {
    chrome.storage.local.set({ [TOKEN_KEY]: token });
  }

  const el = document.querySelector(`[${USER_ID_ATTR}]`);
  const userId = el?.getAttribute(USER_ID_ATTR)?.trim();
  if (userId) {
    chrome.storage.sync.set({ enabled: true });
  }

  markExtensionReady();
}

syncSessionFromPage();

const observer = new MutationObserver(() => {
  syncSessionFromPage();
});
observer.observe(document.documentElement, {
  attributes: true,
  subtree: true,
  attributeFilter: [USER_ID_ATTR],
});

window.addEventListener('storage', (event) => {
  if (event.key === DASHBOARD_TOKEN_KEY && event.newValue) {
    chrome.storage.local.set({ [TOKEN_KEY]: event.newValue });
  }
});

window.addEventListener('message', (event) => {
  if (event.source !== window || event.data?.type !== 'time-tracker-ping') return;
  chrome.runtime.sendMessage({ type: 'get-status' }, (response) => {
    window.postMessage(
      {
        type: 'time-tracker-status',
        ...(response ?? { connected: false }),
        connected: !chrome.runtime.lastError,
      },
      window.location.origin,
    );
  });
});

```

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