alanagoyal/alanagoyal

apple-inspired personal website

TypeScript

333

1,902 commits

updated Oct 19, 2026

See the code

See what people are saying

SourceMessageScoreDate

iMessage-inspired website to chat with famous tech people

1

Jan 22, 2025

i added better error handling for this :) https://www.alanagoyal.com/error

on Show HN: Personal website inspired by Apple notes

0

Jul 12, 2024

https://www.alanagoyal.com/new-note-4eec2a7d-8b44-4e5c-a4ad-... :)

on Show HN: Personal website inspired by Apple notes

0

Jul 11, 2024

Show HN: Personal website inspired by Apple notes

66

Jul 9, 2024

README

alanagoyal.com

i'm obsessed with re-creating apple products. this is a macos-inspired personal website featuring a full desktop environment with multiple apps.

features

desktop environment

a macos sierra 10.12 themed desktop with:

  • window management: draggable, resizable windows with minimize, maximize, and close
  • dock: app launcher with hover tooltips
  • menu bar: functional apple menu, file menu, app menu, status menus (wifi, bluetooth, control center), and notification center with live widgets (calendar, messages, weather, photos)
  • system states: lock screen, sleep mode, restart, and shutdown overlays

apps

notes - apple notes clone for my personal website content

  • public notes viewable by everyone, private notes per browser session
  • github flavored markdown with interactive task lists
  • image paste/upload support
  • swipe gestures on mobile

messages - imessage clone with ai-powered conversations

  • chat with ai contacts that have unique personalities (powered by gpt)
  • message reactions and sound effects
  • typing indicators and read/unread states
  • group chats and one-on-one conversations
  • pinned conversations and swipe gestures
  • @mentions and contact management
  • command menu (⌘K) with keyboard shortcuts
  • focus mode integration (mutes notifications)

iterm - terminal emulator

  • real file system navigation
  • github integration (browse your repos)
  • basic shell commands (ls, cd, cat, pwd, clear, etc.)

textedit - plain text editor

  • opens existing text files in their own windows
  • direct opens without a file use the same Finder picker flow
  • launching from Finder focuses an open TextEdit document window first, otherwise opens a smaller Finder window at Documents

preview - image and pdf viewer

  • opens images and pdfs in their own windows
  • direct opens without a file use the same Finder picker flow
  • launching from Finder focuses an open Preview document window first, otherwise opens a smaller Finder window at Desktop
  • document-app launch behavior is documented in docs/document-apps.md

finder - file browser

  • sidebar navigation (recents, applications, desktop, documents, downloads, projects)
  • browse local files and github repositories
  • multiple Finder windows on desktop
  • launch apps from applications folder

calendar - apple calendar clone

  • day, week, month, and year views with smooth navigation
  • sample events: exercise, focus time, meetings, dinners
  • date night saturdays cycling through sf restaurants
  • create, edit, and delete your own events
  • drag-to-create events in day/week views
  • holidays automatically displayed
  • data persisted in localstorage

photos - apple photos clone

  • photo library with grid view and full-screen viewer
  • collections: flowers, food, friends
  • favorites (per-browser, stored in localstorage)
  • set a library photo as the persistent desktop and lock-screen wallpaper
  • time filters (today, this week, this month, this year, all)
  • keyboard navigation (arrow keys, escape to close)
  • upload via ios shortcut with ai auto-categorization

games - an Apple Games-inspired library with six playable games

  • library layout with sorting, play history, and app-style game artwork
  • snake, 2048, minesweeper, memory match, and breakout run entirely on-device
  • local computer play with easy, medium, and hard opponents
  • anonymous visitor matchmaking with temporary display names, reconnect, and expiry handling
  • live waiting-player badge in the dock
  • desktop integration with the dock, finder, and window system

settings - system preferences

  • wi-fi and bluetooth panels
  • appearance (light/dark/system theme)
  • wallpaper picker with theme wallpapers and photos library support
  • menu bar appearance and clock format options
  • airdrop and focus mode toggles
  • about this mac

weather - weather forecast app

  • opens from notification center weather widget or /weather
  • current conditions with hourly forecast
  • non-dock app (appears in dock only while open)
  • shared weather scene renderer and palettes documented in docs/weather-scenes.md

mobile

mobile support is intentionally app-specific. supported apps render as touch-optimized, full-screen experiences; every unsupported app route redirects to /notes on mobile. Unsupported apps are desktop-only components and do not retain separate mobile presenters.

appin Dock by defaultmobile supportmobile route behavior
Finderyesnoredirects to /notes
Notesyesyesopens Notes
Messagesyesyesopens Messages
Photosyesyesopens Photos
Musicyesyesopens Music
Calendaryesyesopens Calendar
Weathernonoredirects to /notes
iTermyesnoredirects to /notes
Gamesyesnoredirects to /notes
Settingsyesnoredirects to /notes
TextEditnonoredirects to /notes
Previewnonoredirects to /notes

supported mobile apps include:

  • swipe gestures for navigation
  • touch-optimized controls
  • app-specific full-screen layouts

how it works

architecture

the app uses next.js app router with a route group for the desktop environment. on desktop screens, all apps render in windows on a shared desktop. on mobile, apps display fullscreen with navigation.

notes use a session-based architecture:

  • public notes: managed by the site owner, visible to everyone
  • private notes: each browser session gets a unique id (stored in localstorage) linking to notes you create

messages are client-side only:

  • conversations stored in localstorage
  • ai responses generated via braintrust proxy (openai-compatible)
  • no server-side message storage

photos use supabase storage:

  • images stored in supabase storage bucket
  • metadata (filename, timestamp, collections) in database
  • embedded camera and exposure metadata is read from each image on demand in the desktop Info panel and mobile swipe-up details; GPS remains unused
  • favorites are per-browser (stored in localstorage)
  • upload via api with ai auto-categorization (openai gpt-4o-mini)

games uses a browser-local identity and supabase-backed chess matches:

  • chess rules and computer play use chess.js; the computer search runs in a web worker
  • the server validates online moves and uses optimistic versions to reject conflicting updates
  • synchronized match state is persisted as FEN, PGN, and move history; heartbeats distinguish reconnects from abandonment

the app is built with:

  • next.js with app router
  • typescript for type safety
  • supabase for notes database
  • braintrust for ai chat responses (openai-compatible proxy)
  • react-markdown with github flavored markdown
  • tailwind css for styling

backend

the app uses supabase for the postgresql database with row-level security policies to control access to public and private notes.

database schema:

the notes table stores all notes with these fields:

  • id (uuid): unique identifier
  • title (text): note title
  • content (text): markdown content
  • session_id (uuid): links notes to browser sessions
  • public (boolean): controls visibility
  • slug (text): url-friendly identifier
  • category (text): optional categorization
  • emoji (text): note icon
  • created_at (timestamp): when the note was created

caching

public notes are cached for 24 hours using next.js isr. private notes are always real-time.

to manually revalidate public notes:

set REVALIDATE_TOKEN in environment variables, then:

# revalidate sidebar (when adding/removing public notes)
curl -X POST "https://yourdomain.com/notes/revalidate" \
  -H "Content-Type: application/json" \
  -H "x-revalidate-token: your-token" \
  -d '{"layout": true}'

# revalidate specific note (when updating content)
curl -X POST "https://yourdomain.com/notes/revalidate" \
  -H "Content-Type: application/json" \
  -H "x-revalidate-token: your-token" \
  -d '{"slug": "note-slug"}'

or redeploy on vercel to refresh all pages.

REVALIDATE_TOKEN must stay server-side only. do not expose it via NEXT_PUBLIC_* variables.

ios shortcut for photos

upload photos directly from your iphone using the share sheet:

  1. open the shortcuts app on ios
  2. create a new shortcut with these actions:
    • receive images from share sheet
    • get details of image → date taken
    • resize image to max 2048px (fit)
    • convert image to jpeg (quality 0.8)
    • encode with base64
    • format date → iso 8601
    • get contents of url:
      • url: https://yourdomain.com/api/photos/upload
      • method: POST
      • headers: x-api-key: <your-PHOTOS_UPLOAD_API_KEY>
      • body: json { "image": [base64], "timestamp": [formatted date] }
  3. name it "add to website"
  4. enable "show in share sheet" for images

when you share a photo, the shortcut uploads it to supabase storage and ai automatically categorizes it into collections (flowers, food, friends).

clone the repo

git clone https://github.com/alanagoyal/alanagoyal

set up the database

this project uses supabase as a backend. to set up the database:

  1. create a new project and enter your project details
  2. wait for the database to launch
  3. navigate to the sql editor in the dashboard
  4. paste the sql from the migration file into the sql editor and press run

alternatively, use the supabase cli to apply the complete migration history. this is required for online chess because the games migrations create the private match table and its server-only matchmaking function:

supabase db push

grab the project url and anon key from the api settings and put them in a new .env.local file in the root directory:

# supabase (required for notes and photos)
NEXT_PUBLIC_SUPABASE_URL="<your-supabase-url>"
NEXT_PUBLIC_SUPABASE_ANON_KEY="<your-anon-key>"
SUPABASE_SERVICE_ROLE_KEY="<your-service-role-key>"

# braintrust (required for messages ai)
BRAINTRUST_API_KEY="<your-braintrust-api-key>"

# photos upload (required for ios shortcut)
PHOTOS_UPLOAD_API_KEY="<generate-random-key>"
OPENAI_API_KEY="<your-openai-api-key>"

# site config (optional)
NEXT_PUBLIC_SITE_URL="https://yourdomain.com"
REVALIDATE_TOKEN="<your-revalidate-token>"

notes:

  • GITHUB_TOKEN is optional but helps avoid rate limits when using iterm/finder github integration
  • SUPABASE_SERVICE_ROLE_KEY is needed server-side for photo uploads and online chess; never expose it with a NEXT_PUBLIC_ prefix
  • online chess keeps completed and expired matches for seven days, then prunes them during subsequent matchmaking
  • OPENAI_API_KEY is used for ai photo categorization

install dependencies

npm install

run the app

run the application in the command line and it will be available at http://localhost:3000.

npm run dev

run the messages eval

with BRAINTRUST_API_KEY configured in .env.local, run the Messages group-chat behavior eval:

npm run eval:chat

the eval exercises response routing, waiting for the human, conversation wrap-up, schema validity, repetition, and texting brevity against the same prompt, tools, and model used by the production chat route.

deploy

deploy using vercel

markdown syntax for notes

notes support github flavored markdown (gfm) with interactive features. here's what you can use:

headings

# heading 1
## heading 2
### heading 3

text formatting

**bold text**
*italic text*
~~strikethrough~~
`inline code`

lists

unordered lists:

- item one
- item two
  - nested item
  - another nested item

ordered lists:

1. first item
2. second item
3. third item

task lists (interactive)

task lists are interactive - click checkboxes to toggle completion:

- [ ] task to do
- [x] completed task
- [ ] another task

the app automatically updates the markdown when you click checkboxes, so your progress is saved.

tables

create tables using standard markdown table syntax. tables render with a styled dark theme:

| book | author | year read |
|------|--------|-----------|
| the great gatsby | f. scott fitzgerald | 2023 |
| 1984 | george orwell | 2024 |

this renders as:

bookauthoryear read
the great gatsbyf. scott fitzgerald2023
1984george orwell2024

table features:

  • white borders on dark background
  • properly padded cells
  • header row styling
  • responsive layout
  • supports links in cells
[link text](https://example.com)

all links automatically open in new tabs for better navigation.

code blocks

inline code: use backticks for inline code

code blocks: use triple backticks for multi-line code

```javascript
function hello() {
  console.log("hello world");
}
```

blockquotes

> this is a blockquote
> it can span multiple lines

images

paste images directly into notes by copying any image (screenshot, file, etc.) and pressing ctrl+v (or cmd+v on mac). images are automatically uploaded to supabase storage and inserted as markdown.

you can also manually add images:

![alt text](image-url.jpg)

supported formats: jpeg, png, gif, webp (including animated gifs) file size limit: 5mb images are automatically resized to fit the note width while maintaining aspect ratio

horizontal rules

---

license

licensed under the mit license.

alanagoyal/alanagoyal

apple-inspired personal website

TypeScript

333

1,902 commits

updated Oct 19, 2026

See the code

See what people are saying

SourceMessageScoreDate

iMessage-inspired website to chat with famous tech people

1

Jan 22, 2025

i added better error handling for this :) https://www.alanagoyal.com/error

on Show HN: Personal website inspired by Apple notes

0

Jul 12, 2024

https://www.alanagoyal.com/new-note-4eec2a7d-8b44-4e5c-a4ad-... :)

on Show HN: Personal website inspired by Apple notes

0

Jul 11, 2024

Show HN: Personal website inspired by Apple notes

66

Jul 9, 2024

README

alanagoyal.com

i'm obsessed with re-creating apple products. this is a macos-inspired personal website featuring a full desktop environment with multiple apps.

features

desktop environment

a macos sierra 10.12 themed desktop with:

  • window management: draggable, resizable windows with minimize, maximize, and close
  • dock: app launcher with hover tooltips
  • menu bar: functional apple menu, file menu, app menu, status menus (wifi, bluetooth, control center), and notification center with live widgets (calendar, messages, weather, photos)
  • system states: lock screen, sleep mode, restart, and shutdown overlays

apps

notes - apple notes clone for my personal website content

  • public notes viewable by everyone, private notes per browser session
  • github flavored markdown with interactive task lists
  • image paste/upload support
  • swipe gestures on mobile

messages - imessage clone with ai-powered conversations

  • chat with ai contacts that have unique personalities (powered by gpt)
  • message reactions and sound effects
  • typing indicators and read/unread states
  • group chats and one-on-one conversations
  • pinned conversations and swipe gestures
  • @mentions and contact management
  • command menu (⌘K) with keyboard shortcuts
  • focus mode integration (mutes notifications)

iterm - terminal emulator

  • real file system navigation
  • github integration (browse your repos)
  • basic shell commands (ls, cd, cat, pwd, clear, etc.)

textedit - plain text editor

  • opens existing text files in their own windows
  • direct opens without a file use the same Finder picker flow
  • launching from Finder focuses an open TextEdit document window first, otherwise opens a smaller Finder window at Documents

preview - image and pdf viewer

  • opens images and pdfs in their own windows
  • direct opens without a file use the same Finder picker flow
  • launching from Finder focuses an open Preview document window first, otherwise opens a smaller Finder window at Desktop
  • document-app launch behavior is documented in docs/document-apps.md

finder - file browser

  • sidebar navigation (recents, applications, desktop, documents, downloads, projects)
  • browse local files and github repositories
  • multiple Finder windows on desktop
  • launch apps from applications folder

calendar - apple calendar clone

  • day, week, month, and year views with smooth navigation
  • sample events: exercise, focus time, meetings, dinners
  • date night saturdays cycling through sf restaurants
  • create, edit, and delete your own events
  • drag-to-create events in day/week views
  • holidays automatically displayed
  • data persisted in localstorage

photos - apple photos clone

  • photo library with grid view and full-screen viewer
  • collections: flowers, food, friends
  • favorites (per-browser, stored in localstorage)
  • set a library photo as the persistent desktop and lock-screen wallpaper
  • time filters (today, this week, this month, this year, all)
  • keyboard navigation (arrow keys, escape to close)
  • upload via ios shortcut with ai auto-categorization

games - an Apple Games-inspired library with six playable games

  • library layout with sorting, play history, and app-style game artwork
  • snake, 2048, minesweeper, memory match, and breakout run entirely on-device
  • local computer play with easy, medium, and hard opponents
  • anonymous visitor matchmaking with temporary display names, reconnect, and expiry handling
  • live waiting-player badge in the dock
  • desktop integration with the dock, finder, and window system

settings - system preferences

  • wi-fi and bluetooth panels
  • appearance (light/dark/system theme)
  • wallpaper picker with theme wallpapers and photos library support
  • menu bar appearance and clock format options
  • airdrop and focus mode toggles
  • about this mac

weather - weather forecast app

  • opens from notification center weather widget or /weather
  • current conditions with hourly forecast
  • non-dock app (appears in dock only while open)
  • shared weather scene renderer and palettes documented in docs/weather-scenes.md

mobile

mobile support is intentionally app-specific. supported apps render as touch-optimized, full-screen experiences; every unsupported app route redirects to /notes on mobile. Unsupported apps are desktop-only components and do not retain separate mobile presenters.

appin Dock by defaultmobile supportmobile route behavior
Finderyesnoredirects to /notes
Notesyesyesopens Notes
Messagesyesyesopens Messages
Photosyesyesopens Photos
Musicyesyesopens Music
Calendaryesyesopens Calendar
Weathernonoredirects to /notes
iTermyesnoredirects to /notes
Gamesyesnoredirects to /notes
Settingsyesnoredirects to /notes
TextEditnonoredirects to /notes
Previewnonoredirects to /notes

supported mobile apps include:

  • swipe gestures for navigation
  • touch-optimized controls
  • app-specific full-screen layouts

how it works

architecture

the app uses next.js app router with a route group for the desktop environment. on desktop screens, all apps render in windows on a shared desktop. on mobile, apps display fullscreen with navigation.

notes use a session-based architecture:

  • public notes: managed by the site owner, visible to everyone
  • private notes: each browser session gets a unique id (stored in localstorage) linking to notes you create

messages are client-side only:

  • conversations stored in localstorage
  • ai responses generated via braintrust proxy (openai-compatible)
  • no server-side message storage

photos use supabase storage:

  • images stored in supabase storage bucket
  • metadata (filename, timestamp, collections) in database
  • embedded camera and exposure metadata is read from each image on demand in the desktop Info panel and mobile swipe-up details; GPS remains unused
  • favorites are per-browser (stored in localstorage)
  • upload via api with ai auto-categorization (openai gpt-4o-mini)

games uses a browser-local identity and supabase-backed chess matches:

  • chess rules and computer play use chess.js; the computer search runs in a web worker
  • the server validates online moves and uses optimistic versions to reject conflicting updates
  • synchronized match state is persisted as FEN, PGN, and move history; heartbeats distinguish reconnects from abandonment

the app is built with:

  • next.js with app router
  • typescript for type safety
  • supabase for notes database
  • braintrust for ai chat responses (openai-compatible proxy)
  • react-markdown with github flavored markdown
  • tailwind css for styling

backend

the app uses supabase for the postgresql database with row-level security policies to control access to public and private notes.

database schema:

the notes table stores all notes with these fields:

  • id (uuid): unique identifier
  • title (text): note title
  • content (text): markdown content
  • session_id (uuid): links notes to browser sessions
  • public (boolean): controls visibility
  • slug (text): url-friendly identifier
  • category (text): optional categorization
  • emoji (text): note icon
  • created_at (timestamp): when the note was created

caching

public notes are cached for 24 hours using next.js isr. private notes are always real-time.

to manually revalidate public notes:

set REVALIDATE_TOKEN in environment variables, then:

# revalidate sidebar (when adding/removing public notes)
curl -X POST "https://yourdomain.com/notes/revalidate" \
  -H "Content-Type: application/json" \
  -H "x-revalidate-token: your-token" \
  -d '{"layout": true}'

# revalidate specific note (when updating content)
curl -X POST "https://yourdomain.com/notes/revalidate" \
  -H "Content-Type: application/json" \
  -H "x-revalidate-token: your-token" \
  -d '{"slug": "note-slug"}'

or redeploy on vercel to refresh all pages.

REVALIDATE_TOKEN must stay server-side only. do not expose it via NEXT_PUBLIC_* variables.

ios shortcut for photos

upload photos directly from your iphone using the share sheet:

  1. open the shortcuts app on ios
  2. create a new shortcut with these actions:
    • receive images from share sheet
    • get details of image → date taken
    • resize image to max 2048px (fit)
    • convert image to jpeg (quality 0.8)
    • encode with base64
    • format date → iso 8601
    • get contents of url:
      • url: https://yourdomain.com/api/photos/upload
      • method: POST
      • headers: x-api-key: <your-PHOTOS_UPLOAD_API_KEY>
      • body: json { "image": [base64], "timestamp": [formatted date] }
  3. name it "add to website"
  4. enable "show in share sheet" for images

when you share a photo, the shortcut uploads it to supabase storage and ai automatically categorizes it into collections (flowers, food, friends).

clone the repo

git clone https://github.com/alanagoyal/alanagoyal

set up the database

this project uses supabase as a backend. to set up the database:

  1. create a new project and enter your project details
  2. wait for the database to launch
  3. navigate to the sql editor in the dashboard
  4. paste the sql from the migration file into the sql editor and press run

alternatively, use the supabase cli to apply the complete migration history. this is required for online chess because the games migrations create the private match table and its server-only matchmaking function:

supabase db push

grab the project url and anon key from the api settings and put them in a new .env.local file in the root directory:

# supabase (required for notes and photos)
NEXT_PUBLIC_SUPABASE_URL="<your-supabase-url>"
NEXT_PUBLIC_SUPABASE_ANON_KEY="<your-anon-key>"
SUPABASE_SERVICE_ROLE_KEY="<your-service-role-key>"

# braintrust (required for messages ai)
BRAINTRUST_API_KEY="<your-braintrust-api-key>"

# photos upload (required for ios shortcut)
PHOTOS_UPLOAD_API_KEY="<generate-random-key>"
OPENAI_API_KEY="<your-openai-api-key>"

# site config (optional)
NEXT_PUBLIC_SITE_URL="https://yourdomain.com"
REVALIDATE_TOKEN="<your-revalidate-token>"

notes:

  • GITHUB_TOKEN is optional but helps avoid rate limits when using iterm/finder github integration
  • SUPABASE_SERVICE_ROLE_KEY is needed server-side for photo uploads and online chess; never expose it with a NEXT_PUBLIC_ prefix
  • online chess keeps completed and expired matches for seven days, then prunes them during subsequent matchmaking
  • OPENAI_API_KEY is used for ai photo categorization

install dependencies

npm install

run the app

run the application in the command line and it will be available at http://localhost:3000.

npm run dev

run the messages eval

with BRAINTRUST_API_KEY configured in .env.local, run the Messages group-chat behavior eval:

npm run eval:chat

the eval exercises response routing, waiting for the human, conversation wrap-up, schema validity, repetition, and texting brevity against the same prompt, tools, and model used by the production chat route.

deploy

deploy using vercel

markdown syntax for notes

notes support github flavored markdown (gfm) with interactive features. here's what you can use:

headings

# heading 1
## heading 2
### heading 3

text formatting

**bold text**
*italic text*
~~strikethrough~~
`inline code`

lists

unordered lists:

- item one
- item two
  - nested item
  - another nested item

ordered lists:

1. first item
2. second item
3. third item

task lists (interactive)

task lists are interactive - click checkboxes to toggle completion:

- [ ] task to do
- [x] completed task
- [ ] another task

the app automatically updates the markdown when you click checkboxes, so your progress is saved.

tables

create tables using standard markdown table syntax. tables render with a styled dark theme:

| book | author | year read |
|------|--------|-----------|
| the great gatsby | f. scott fitzgerald | 2023 |
| 1984 | george orwell | 2024 |

this renders as:

bookauthoryear read
the great gatsbyf. scott fitzgerald2023
1984george orwell2024

table features:

  • white borders on dark background
  • properly padded cells
  • header row styling
  • responsive layout
  • supports links in cells
[link text](https://example.com)

all links automatically open in new tabs for better navigation.

code blocks

inline code: use backticks for inline code

code blocks: use triple backticks for multi-line code

```javascript
function hello() {
  console.log("hello world");
}
```

blockquotes

> this is a blockquote
> it can span multiple lines

images

paste images directly into notes by copying any image (screenshot, file, etc.) and pressing ctrl+v (or cmd+v on mac). images are automatically uploaded to supabase storage and inserted as markdown.

you can also manually add images:

![alt text](image-url.jpg)

supported formats: jpeg, png, gif, webp (including animated gifs) file size limit: 5mb images are automatically resized to fit the note width while maintaining aspect ratio

horizontal rules

---

license

licensed under the mit license.

Significant stargazers

Jack Morris

601 followers · starred Jun 2026

Danilo Leal

347 followers · starred Jul 2024

Andrew Smith

459 followers · starred Jul 2024

Rhys Sullivan

950 followers · starred Nov 2024