feat: Redirect /presentations to slides.jeffemmett.com

slides.jeffemmett.com is now the canonical home for presentations.
Server-side redirect via next.config.mjs + client-side fallback
preserving hash fragments. Hero button links directly to new domain.

Also: clean up v0/Vercel references from README and package.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-02-13 09:42:19 -07:00
parent c2147fd3dc
commit 24197d859a
5 changed files with 21 additions and 283 deletions

View File

@ -1,30 +1 @@
# Personal website redesign
*Automatically synced with your [v0.app](https://v0.app) deployments*
[![Deployed on Vercel](https://img.shields.io/badge/Deployed%20on-Vercel-black?style=for-the-badge&logo=vercel)](https://vercel.com/jeff-emmetts-projects/v0-personal-website-redesign)
[![Built with v0](https://img.shields.io/badge/Built%20with-v0.app-black?style=for-the-badge)](https://v0.app/chat/c8jGR8t3eSX)
## Overview
This repository will stay in sync with your deployed chats on [v0.app](https://v0.app).
Any changes you make to your deployed app will be automatically pushed to this repository from [v0.app](https://v0.app).
## Deployment
Your project is live at:
**[https://vercel.com/jeff-emmetts-projects/v0-personal-website-redesign](https://vercel.com/jeff-emmetts-projects/v0-personal-website-redesign)**
## Build your app
Continue building your app on:
**[https://v0.app/chat/c8jGR8t3eSX](https://v0.app/chat/c8jGR8t3eSX)**
## How It Works
1. Create and modify your project using [v0.app](https://v0.app)
2. Deploy your chats from the v0 interface
3. Changes are automatically pushed to this repository
4. Vercel deploys the latest version from this repository

View File

@ -1,261 +1,15 @@
"use client"
import { useEffect } from "react"
import { Link2, ExternalLink, ArrowLeft } from "lucide-react"
import { Card } from "@/components/ui/card"
import { CursorEffect } from "@/components/cursor-effect"
import { toast } from "sonner"
const presentations = [
{
id: "osmotic-governance",
title: "Osmotic Governance",
description: "Exploring the intersection of mycelium and emancipatory technologies",
embedUrl: "https://slides.jeffemmett.com/osmotic-governance",
venue: "Team Human with Douglas Rushkoff",
videoUrl: "https://www.teamhuman.fm/episodes/238-jeff-emmett",
videoLabel: "Listen to the full episode",
tags: ["Mycelium", "Governance", "Emancipatory Tech"],
},
{
id: "exploring-mycofi",
title: "Exploring MycoFi",
description: "Mycelial design patterns for Web3 and beyond",
embedUrl: "https://slides.jeffemmett.com/exploring-mycofi",
venue: "DevCon 7 in Bangkok",
videoUrl: "https://www.youtube.com/watch?v=0A4jXL5eBaI",
videoLabel: "Watch the full talk",
tags: ["MycoFi", "Web3", "Biomimicry"],
},
{
id: "mycofi-cofi-gathering",
title: "MycoFi talk at CoFi Gathering",
description: "Mycelial design patterns for Web3 and beyond",
embedUrl: "https://slides.jeffemmett.com/mycofi-cofi-gathering",
venue: "Greenpill Network",
videoUrl: "https://www.youtube.com/watch?v=AFJFDajuCSg",
videoLabel: "Watch the full talk",
tags: ["MycoFi", "Greenpill", "CoFi"],
},
{
id: "myco-mutualism",
title: "Myco-Mutualism",
description: "Exploring mutualistic relationships in mycelial networks and their applications to human systems",
embedUrl: "https://slides.jeffemmett.com/myco-mutualism",
venue: "The Mutualist Society",
tags: ["Mutualism", "Mycelium", "Systems Design"],
},
{
id: "psilocybernetics",
title: "Psilocybernetics: The Emergence of Institutional Neuroplasticity",
description: "Exploring the intersection of mycelium and cybernetic institutional design",
embedUrl: "https://slides.jeffemmett.com/psilocybernetics",
venue: "General Forum for Ethereum Localism",
tags: ["Cybernetics", "Institutions", "Neuroplasticity"],
},
{
id: "move-slow-fix-things",
title: "Move Slow & Fix Things: The Commons Stack Design Pattern",
description: "Design patterns for sustainable commons infrastructure",
embedUrl: "https://slides.jeffemmett.com/move-slow-fix-things",
venue: "ReFi Unconf @ the Commons Hub Austria",
videoUrl: "https://www.youtube.com/live/i8qcg7FfpLM?si=onLcl8q5rz7cMViO&t=1362",
videoLabel: "Watch the full talk",
tags: ["Commons Stack", "ReFi", "Design Patterns"],
},
{
id: "commons-stack-launch",
title: "Commons Stack Launch & Open Sourcing cadCAD",
description: "The launch of Commons Stack and the open sourcing of cadCAD for token engineering",
embedUrl: "https://slides.jeffemmett.com/commons-stack-launch",
venue: "Token Engineering Global Gathering (TEGG)",
videoUrl: "https://youtu.be/qjdjX2m_p0Q?si=r2AXVnVyzCIxIOSc&t=20",
videoLabel: "Watch the full talk",
tags: ["Commons Stack", "cadCAD", "Token Engineering"],
},
{
id: "conviction-voting",
title: "New Tools for Dynamic Collective Intelligence: Conviction Voting & Variations",
description: "Exploring innovative voting mechanisms for collective decision-making in decentralized systems",
embedUrl: "https://slides.jeffemmett.com/conviction-voting",
venue: "Conviction Voting Presentation",
tags: ["Conviction Voting", "Governance", "Collective Intelligence"],
},
{
id: "polycentric-governance",
title: "Exploring Polycentric Governance in Web3 Ecosystems",
description: "Understanding multi-level governance structures in decentralized networks",
embedUrl: "https://slides.jeffemmett.com/polycentric-governance",
venue: "OpenWeb Hackathon",
videoUrl: "https://youtu.be/ZIWskNogafg?si=DmUbOQJaSRE1rdzq",
videoLabel: "Watch the full talk",
tags: ["Polycentric Governance", "Web3", "Ostrom"],
},
{
id: "mycofi-myco-munnities",
title: "MycoFi for Myco-munnities",
description: "Exploring mycelial financial systems for community-based organizations",
embedUrl: "https://slides.jeffemmett.com/mycofi-myco-munnities",
venue: "CoFi Gathering in Liege",
tags: ["MycoFi", "Community Finance", "CoFi"],
},
{
id: "community-resilience",
title: "Building Community Resilience in an Age of Crisis",
description: "Internet outages during crises can disrupt communication, education, and access to vital information. Preparing for such disruptions is essential for communities operating in challenging environments.",
embedUrl: "https://slides.jeffemmett.com/community-resilience",
venue: "re:publica conference, May 2025",
videoUrl: "https://www.youtube.com/watch?v=rTOLk7k9Ad8",
videoLabel: "Watch the full talk",
tags: ["Resilience", "Crisis Response", "Community Networks"],
},
]
function copyLink(id: string) {
const url = `${window.location.origin}/presentations#${id}`
navigator.clipboard.writeText(url).then(() => {
toast.success("Link copied to clipboard")
})
}
export default function PresentationsPage() {
export default function PresentationsRedirect() {
useEffect(() => {
if (window.location.hash) {
const id = window.location.hash.slice(1)
const el = document.getElementById(id)
if (el) {
setTimeout(() => {
el.scrollIntoView({ behavior: "smooth", block: "start" })
}, 100)
}
}
window.location.replace(`https://slides.jeffemmett.com/${window.location.hash}`)
}, [])
return (
<>
<CursorEffect />
<main className="relative min-h-screen bg-background text-foreground">
{/* Animated background blobs */}
<div className="fixed inset-0 overflow-hidden opacity-15 pointer-events-none">
<div className="absolute top-1/4 left-1/3 h-[500px] w-[500px] rounded-full bg-primary blur-[150px] animate-float" />
<div
className="absolute bottom-1/3 right-1/4 h-[400px] w-[400px] rounded-full bg-accent blur-[150px]"
style={{ animationDelay: "3s" }}
/>
</div>
{/* Header */}
<div className="relative z-10 border-b border-border/50 bg-background/80 backdrop-blur-sm">
<div className="max-w-5xl mx-auto px-6 py-4 flex items-center justify-between">
<a
href="/"
className="inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-primary transition-colors"
>
<ArrowLeft className="h-4 w-4" />
Jeff Emmett
</a>
<span className="text-sm font-mono text-muted-foreground border border-border px-3 py-1 rounded-full">
{"{ Presentations }"}
</span>
</div>
</div>
{/* Hero */}
<div className="relative z-10 max-w-5xl mx-auto px-6 pt-20 pb-16">
<h1 className="text-5xl md:text-6xl font-bold mb-6">Presentations</h1>
<p className="text-xl text-muted-foreground max-w-2xl text-balance leading-relaxed">
Research into the intersection of mycelium networks, emancipatory technologies,
and convivial tooling. Designing systems that support collective action and
community self-organization.
</p>
</div>
{/* Presentations grid */}
<div className="relative z-10 max-w-5xl mx-auto px-6 pb-32">
<div className="space-y-10">
{presentations.map((p) => (
<Card
key={p.id}
id={p.id}
className="group/card overflow-hidden bg-card/80 backdrop-blur-sm border-border hover:border-primary/50 transition-all duration-300 scroll-mt-24"
>
<div className="p-6 pb-4">
<h2 className="text-2xl font-semibold group-hover/card:text-primary transition-colors">
<a
href={`#${p.id}`}
onClick={(e) => {
e.preventDefault()
window.history.replaceState(null, "", `#${p.id}`)
document.getElementById(p.id)?.scrollIntoView({ behavior: "smooth", block: "start" })
copyLink(p.id)
}}
className="group/link inline-flex items-start gap-2"
>
<span>{p.title}</span>
<Link2 className="h-5 w-5 mt-1 opacity-0 group-hover/link:opacity-100 text-muted-foreground transition-opacity shrink-0" />
</a>
</h2>
<p className="text-muted-foreground mt-2 leading-relaxed">{p.description}</p>
{p.tags && (
<div className="flex flex-wrap gap-2 mt-3">
{p.tags.map((tag, i) => (
<span
key={i}
className="text-xs font-mono px-3 py-1 bg-secondary text-secondary-foreground rounded-full"
>
{tag}
</span>
))}
</div>
)}
</div>
<div className="px-6 pb-4">
<div className="rounded-lg overflow-hidden border border-border/50">
<div style={{ position: "relative", paddingTop: "max(60%, 324px)", width: "100%", height: 0 }}>
<iframe
style={{ position: "absolute", border: "none", width: "100%", height: "100%", left: 0, top: 0 }}
src={p.embedUrl}
seamless
scrolling="no"
frameBorder="0"
allowTransparency
allowFullScreen
title={p.title}
/>
</div>
</div>
</div>
<div className="px-6 pb-6 flex flex-wrap items-center justify-between gap-3 border-t border-border/30 pt-4 mx-6">
<span className="text-sm text-muted-foreground italic font-mono">{p.venue}</span>
{p.videoUrl ? (
<a
href={p.videoUrl}
target="_blank"
rel="noopener noreferrer"
className="group/ext inline-flex items-center gap-1.5 text-sm font-medium px-4 py-2 bg-secondary text-secondary-foreground rounded-lg transition-all hover:scale-105 hover:bg-primary hover:text-primary-foreground"
>
{p.videoLabel} <ExternalLink className="h-3.5 w-3.5" />
</a>
) : (
<span className="text-sm text-muted-foreground">Video coming soon</span>
)}
</div>
</Card>
))}
</div>
</div>
{/* Footer */}
<div className="relative z-10 border-t border-border py-8 bg-card/30">
<div className="max-w-5xl mx-auto px-6 text-center">
<p className="text-muted-foreground font-mono text-sm">
© 2025 Jeff Emmett. Built with Next.js & TailwindCSS
</p>
</div>
</div>
</main>
</>
<div className="flex items-center justify-center min-h-screen bg-background text-muted-foreground">
<p>Redirecting to slides.jeffemmett.com...</p>
</div>
)
}

View File

@ -63,7 +63,7 @@ export function HeroSection() {
<div className="absolute inset-0 bg-accent opacity-0 group-hover:opacity-100 transition-opacity" />
</a>
<a
href="/presentations"
href="https://slides.jeffemmett.com"
className="px-8 py-4 bg-secondary text-secondary-foreground rounded-lg font-medium transition-all hover:scale-105 hover:bg-secondary/80"
>
Presentations

View File

@ -7,6 +7,20 @@ const nextConfig = {
images: {
unoptimized: true,
},
async redirects() {
return [
{
source: '/presentations',
destination: 'https://slides.jeffemmett.com',
permanent: true,
},
{
source: '/presentations/:path*',
destination: 'https://slides.jeffemmett.com/:path*',
permanent: true,
},
]
},
}
export default nextConfig

View File

@ -37,7 +37,6 @@
"@radix-ui/react-toggle": "1.1.1",
"@radix-ui/react-toggle-group": "1.1.1",
"@radix-ui/react-tooltip": "1.1.6",
"@vercel/analytics": "latest",
"autoprefixer": "^10.4.20",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@ -70,4 +69,4 @@
"tw-animate-css": "1.3.3",
"typescript": "^5"
}
}
}