Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,4 @@ jobs:
env:
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
NEXT_PUBLIC_APP_URL: https://pomodoro-jam.vercel.app
NEXT_PUBLIC_APP_URL: https://bonfirefocus.vercel.app
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# PomodoroJam
# Bonfire

A real-time shared Pomodoro timer app built with Next.js 14.

Expand Down
6 changes: 3 additions & 3 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PomodoroJam — Product Roadmap
# Bonfire — Product Roadmap

> Vision: Transform PomodoroJam from a shared timer into a focus social network —
> Vision: Transform Bonfire from a shared timer into a focus social network —
> where sessions become rooms, productivity becomes identity, and your streak with
> a friend is worth sharing.
>
Expand Down Expand Up @@ -277,7 +277,7 @@
> Makes the app sticky for new users who have zero friends on it yet.

### 5a. Onboarding flow
- [ ] First-time visitor sees a 3-step onboarding: what is PomodoroJam, how rooms work, the bonfire mechanic
- [ ] First-time visitor sees a 3-step onboarding: what is Bonfire, how rooms work, the bonfire mechanic
- [ ] "Aha moment" designed in: show a live room with the fire burning before they even sign up
- [ ] Skip-friendly — should never feel forced
- [ ] After sign-up: prompt to follow 1-2 suggested users (seeded accounts that are always "active")
Expand Down
10 changes: 5 additions & 5 deletions app/api/og/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ export async function GET(request: NextRequest) {
const pomodoros = clampInt(searchParams.get('pomodoros'))
const streak = clampInt(searchParams.get('streak'))
const hours = clampInt(searchParams.get('hours'))
const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://pomodoro-jam.vercel.app'
const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'https://bonfirefocus.vercel.app'
const displayUrl = appUrl.replace(/^https?:\/\//, '')

if (type === 'stats') {
return new ImageResponse(
(
<div style={{ background: '#0F0F0D', width: '100%', height: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', fontFamily: 'sans-serif', position: 'relative' }}>
<div style={{ position: 'absolute', width: '600px', height: '600px', borderRadius: '50%', background: 'radial-gradient(circle, rgba(255,85,51,0.1) 0%, transparent 65%)', top: '15px', left: '300px' }} />
<div style={{ fontSize: '20px', color: '#666', marginBottom: '8px' }}>🍅 PomodoroJam</div>
<div style={{ fontSize: '20px', color: '#666', marginBottom: '8px' }}>Bonfire</div>
<div style={{ fontSize: '40px', fontWeight: '800', color: '#fff', marginBottom: '4px' }}>{username}</div>
<div style={{ fontSize: '16px', color: '#888', marginBottom: '48px' }}>Focus stats</div>
<div style={{ display: 'flex', gap: '40px' }}>
Expand Down Expand Up @@ -71,7 +71,7 @@ export async function GET(request: NextRequest) {
<ellipse cx="38" cy="44" rx="9" ry="6" fill="rgba(255,255,255,0.25)" />
</svg>
<div style={{ fontSize: '22px', color: '#888888', marginBottom: '12px', letterSpacing: '0.02em' }}>
PomodoroJam
Bonfire
</div>
<div style={{ fontSize: '52px', fontWeight: '800', color: '#FFFFFF', textAlign: 'center', maxWidth: '900px', lineHeight: '1.2', marginBottom: '16px', padding: '0 40px' }}>
{host} invited you to a focus room
Expand Down Expand Up @@ -146,8 +146,8 @@ export async function GET(request: NextRequest) {
marginBottom: '20px',
}}
>
<span style={{ color: '#FFFFFF' }}>Pomodoro</span>
<span style={{ color: '#FF5533' }}>Jam</span>
<span style={{ color: '#FFFFFF' }}>Bon</span>
<span style={{ color: '#FF5533' }}>fire</span>
</div>

{/* Session name or default tagline */}
Expand Down
3 changes: 3 additions & 0 deletions app/api/session/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const CreateSessionSchema = z.object({
jam_mode: z.boolean().optional(),
session_mode: z.enum(['host', 'jam', 'solo']).optional(),
is_public: z.boolean().optional(),
display_name: z.string().trim().min(1).max(40).nullable().optional(),
})

export async function POST(request: Request) {
Expand Down Expand Up @@ -41,6 +42,8 @@ export async function POST(request: Request) {
if (profile) {
hostName = profile.display_name ?? profile.username ?? 'Guest'
}
} else if (parsed.data.display_name) {
hostName = parsed.data.display_name
}

const sessionId = generateSessionId()
Expand Down
14 changes: 10 additions & 4 deletions app/explore/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Metadata } from 'next'
import Link from 'next/link'
import { Flame } from 'lucide-react'
import { createClient } from '@/lib/supabase/server'
import { Logo } from '@/components/ui/Logo'
import { ThemeToggle } from '@/components/ui/ThemeToggle'
Expand Down Expand Up @@ -139,7 +140,7 @@ export default async function ExplorePage() {
<div className="font-display font-bold text-2xl" style={{ color: 'var(--text-primary)' }}>
{totalFocusing}
</div>
<div className="text-[10px] font-semibold uppercase tracking-widest mt-0.5" style={{ color: 'var(--text-muted)' }}>
<div className="text-xs font-medium mt-0.5" style={{ color: 'var(--text-muted)' }}>
Focusing Now
</div>
</div>
Expand All @@ -152,7 +153,7 @@ export default async function ExplorePage() {
className="flex flex-col items-center justify-center text-center py-24 rounded-3xl"
style={{ background: 'var(--bg-elevated)', border: '1px solid var(--border)' }}
>
<div className="text-5xl mb-4">🍅</div>
<Flame className="w-12 h-12 mb-4" style={{ color: 'var(--accent)' }} />
<h2 className="font-display font-bold text-xl mb-2" style={{ color: 'var(--text-primary)' }}>
No live rooms right now
</h2>
Expand Down Expand Up @@ -200,11 +201,16 @@ export default async function ExplorePage() {
</span>
</div>

{/* Middle: room title */}
{/* Middle: room title + host */}
<div className="flex-1 mb-4">
<h2 className="font-display font-bold text-lg leading-snug" style={{ color: 'var(--text-primary)' }}>
{session.title ?? 'Focus Room'}
</h2>
{session.host_name && (
<p className="text-xs mt-1" style={{ color: 'var(--text-muted)' }}>
hosted by {session.host_name}
</p>
)}
</div>

{/* Bottom: avatar stack + count */}
Expand Down Expand Up @@ -248,7 +254,7 @@ export default async function ExplorePage() {
</div>

<span className="text-xs" style={{ color: 'var(--text-muted)' }}>
{count === 1 ? 'Focusing now' : `${count} focusing${count >= 5 ? ' 🔥' : ''}`}
{count === 1 ? 'Focusing now' : `${count} focusing`}
</span>
</div>
</Link>
Expand Down
4 changes: 2 additions & 2 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

/* Fonts */
--font-dm-sans: 'DM Sans', system-ui, sans-serif;
--font-syne: 'Syne', sans-serif;
--font-display: 'Plus Jakarta Sans', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
--font-space-mono: var(--font-mono); /* backward compat */
}
Expand All @@ -60,7 +60,7 @@
--bg-elevated: #222220;
--text-primary: #F5F5F0;
--text-secondary: #9B9B8E;
--text-muted: #5A5A50;
--text-muted: #7E7E72;
--accent: #FF5533;
--accent-soft: rgba(255, 85, 51, 0.1);
--accent-hover: #FF6644;
Expand Down
36 changes: 19 additions & 17 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Metadata, Viewport } from 'next'
import { DM_Sans, Syne, JetBrains_Mono } from 'next/font/google'
import { DM_Sans, Plus_Jakarta_Sans, JetBrains_Mono } from 'next/font/google'
import { ThemeProvider } from 'next-themes'
import { Analytics } from '@vercel/analytics/next'
import { FaviconInit } from '@/components/ui/FaviconInit'
Expand All @@ -11,9 +11,9 @@ const dmSans = DM_Sans({
display: 'swap',
})

const syne = Syne({
const plusJakartaSans = Plus_Jakarta_Sans({
subsets: ['latin'],
variable: '--font-syne',
variable: '--font-display',
display: 'swap',
})

Expand All @@ -23,53 +23,55 @@ const jetbrainsMono = JetBrains_Mono({
display: 'swap',
})

const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? 'https://pomodoro-jam.vercel.app'
const appUrl = process.env.NEXT_PUBLIC_APP_URL ?? 'https://bonfirefocus.vercel.app'

export const metadata: Metadata = {
metadataBase: new URL(appUrl),
title: {
default: 'PomodoroJam: Focus Together',
template: '%s | PomodoroJam',
default: 'Bonfire: Focus Together',
template: '%s | Bonfire',
},
description:
'A shared Pomodoro timer for friends. Start a room, share the link, focus in sync.',
'A shared focus timer for friends. Start a room, share the link, focus in sync.',
keywords: ['pomodoro', 'focus', 'productivity', 'timer', 'shared', 'real-time'],
authors: [{ name: 'PomodoroJam' }],
creator: 'PomodoroJam',
authors: [{ name: 'Bonfire' }],
creator: 'Bonfire',
openGraph: {
type: 'website',
locale: 'en_US',
url: appUrl,
siteName: 'PomodoroJam',
title: 'PomodoroJam: Focus Together',
siteName: 'Bonfire',
title: 'Bonfire: Focus Together',
description: 'Real-time shared Pomodoro timer. Focus with friends.',
images: [
{
url: '/api/og',
width: 1200,
height: 630,
alt: 'PomodoroJam',
alt: 'Bonfire',
},
],
},
twitter: {
card: 'summary_large_image',
title: 'PomodoroJam: Focus Together',
title: 'Bonfire: Focus Together',
description: 'Real-time shared Pomodoro timer. Focus with friends.',
images: ['/api/og'],
},
manifest: '/manifest.json',
appleWebApp: {
capable: true,
statusBarStyle: 'black-translucent',
title: 'PomodoroJam',
title: 'Bonfire',
},
icons: {
apple: '/apple-touch-icon.png',
},
alternates: {
canonical: appUrl,
},
other: {
'mobile-web-app-capable': 'yes',
},
}

export const viewport: Viewport = {
Expand All @@ -90,7 +92,7 @@ export default function RootLayout({
<html
lang="en"
suppressHydrationWarning
className={`${dmSans.variable} ${syne.variable} ${jetbrainsMono.variable}`}
className={`${dmSans.variable} ${plusJakartaSans.variable} ${jetbrainsMono.variable}`}
>
<body className="bg-background text-foreground font-sans min-h-screen antialiased">
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
Expand All @@ -100,7 +102,7 @@ export default function RootLayout({
__html: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'WebApplication',
name: 'PomodoroJam',
name: 'Bonfire',
url: appUrl,
description: 'A shared Pomodoro timer for friends. Start a session, share the link, focus in sync.',
applicationCategory: 'ProductivityApplication',
Expand Down
Loading