feat: implement instant download functionality with static link (#16)

This commit is contained in:
Owen Gretzinger
2025-07-14 22:32:57 -04:00
committed by GitHub
parent c449847752
commit b6c083ffce
9 changed files with 171 additions and 118 deletions
+8 -2
View File
@@ -75,11 +75,17 @@ export default function Features() {
<div className="bg-gradient-to-r from-blue-600/20 to-purple-600/20 rounded-xl p-8 border border-blue-600/30"> <div className="bg-gradient-to-r from-blue-600/20 to-purple-600/20 rounded-xl p-8 border border-blue-600/30">
<h3 className="text-2xl font-bold mb-4 text-center">Coming Soon</h3> <h3 className="text-2xl font-bold mb-4 text-center">Coming Soon</h3>
<p className="text-gray-300 text-center mb-4"> <p className="text-gray-300 text-center mb-4">
Google Calendar integration, note templates, AI chat for questions, Google Calendar integration, AI chat for questions, and more
and more integrations (email, Slack). integrations (email, Slack).
</p> </p>
<p className="text-blue-400 text-center font-semibold"> <p className="text-blue-400 text-center font-semibold">
<a
href="https://github.com/owengretzinger/meetingnotes"
target="_blank"
className="text-blue-400 hover:text-blue-300 underline transition-colors"
>
Contribute on GitHub to help build these features! Contribute on GitHub to help build these features!
</a>
</p> </p>
</div> </div>
</div> </div>
+7 -6
View File
@@ -1,5 +1,8 @@
"use client"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Download, Github, ArrowRight } from "lucide-react" import { Download, Github, ArrowRight } from "lucide-react"
import { downloadAndNavigate } from "@/lib/utils"
export default function FinalCTA() { export default function FinalCTA() {
return ( return (
@@ -17,12 +20,10 @@ export default function FinalCTA() {
</p> </p>
<div className="flex flex-col sm:flex-row gap-6 justify-center mb-12"> <div className="flex flex-col sm:flex-row gap-6 justify-center mb-12">
<Button size="lg" className="bg-blue-600 hover:bg-blue-700 text-lg px-8 py-4 group" asChild> <Button size="lg" className="bg-blue-600 hover:bg-blue-700 text-lg px-8 py-4 group" onClick={downloadAndNavigate}>
<a href="/download"> <Download className="w-5 h-5 mr-2" />
<Download className="w-5 h-5 mr-2" /> Download for macOS
Download for macOS <ArrowRight className="w-4 h-4 ml-2 group-hover:translate-x-1 transition-transform" />
<ArrowRight className="w-4 h-4 ml-2 group-hover:translate-x-1 transition-transform" />
</a>
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
+7 -4
View File
@@ -1,4 +1,7 @@
"use client"
import { Github, Twitter, Mail, Heart } from "lucide-react"; import { Github, Twitter, Mail, Heart } from "lucide-react";
import { downloadAndNavigate } from "@/lib/utils"
export default function Footer() { export default function Footer() {
return ( return (
@@ -59,12 +62,12 @@ export default function Footer() {
</a> </a>
</li> </li>
<li> <li>
<a <button
href="/download" onClick={downloadAndNavigate}
className="hover:text-white transition-colors" className="hover:text-white transition-colors text-left"
> >
Download Download
</a> </button>
</li> </li>
<li> <li>
<a <a
+6 -5
View File
@@ -1,5 +1,8 @@
"use client"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Github, Download } from "lucide-react" import { Github, Download } from "lucide-react"
import { downloadAndNavigate } from "@/lib/utils"
export default function Header() { export default function Header() {
return ( return (
@@ -44,11 +47,9 @@ export default function Header() {
<span className="hidden md:inline">Star</span> <span className="hidden md:inline">Star</span>
</a> </a>
</Button> </Button>
<Button size="sm" className="bg-blue-600 hover:bg-blue-700" asChild> <Button size="sm" className="bg-blue-600 hover:bg-blue-700" onClick={downloadAndNavigate}>
<a href="/download"> <Download className="w-4 h-4 md:mr-2" />
<Download className="w-4 h-4 md:mr-2" /> <span className="hidden md:inline">Download</span>
<span className="hidden md:inline">Download</span>
</a>
</Button> </Button>
</div> </div>
</div> </div>
+7 -6
View File
@@ -1,6 +1,9 @@
"use client"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { Download, Github, Star, Shield, Code, Zap } from "lucide-react" import { Download, Github, Star, Shield, Code, Zap } from "lucide-react"
import { downloadAndNavigate } from "@/lib/utils"
export default function Hero() { export default function Hero() {
return ( return (
@@ -19,7 +22,7 @@ export default function Hero() {
</h1> </h1>
<p className="text-xl md:text-2xl text-gray-300 mb-8 max-w-[52rem] mx-auto leading-relaxed"> <p className="text-xl md:text-2xl text-gray-300 mb-8 max-w-[52rem] mx-auto leading-relaxed">
Just you and your OpenAI API key.<br />Go crazy. Just you and your OpenAI API key. <br className="block md:hidden" />Go crazy.
</p> </p>
<div className="flex flex-wrap justify-center gap-6 mb-12"> <div className="flex flex-wrap justify-center gap-6 mb-12">
@@ -41,11 +44,9 @@ export default function Hero() {
</div> </div>
<div className="flex flex-col sm:flex-row gap-4 justify-center mb-16"> <div className="flex flex-col sm:flex-row gap-4 justify-center mb-16">
<Button size="lg" className="bg-blue-600 hover:bg-blue-700 text-lg px-8 py-4" asChild> <Button size="lg" className="bg-blue-600 hover:bg-blue-700 text-lg px-8 py-4" onClick={downloadAndNavigate}>
<a href="/download"> <Download className="w-5 h-5 mr-2" />
<Download className="w-5 h-5 mr-2" /> Download for macOS
Download for macOS
</a>
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
+4 -1
View File
@@ -1,6 +1,9 @@
"use client"
import { Card, CardContent } from "@/components/ui/card" import { Card, CardContent } from "@/components/ui/card"
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { Check, DollarSign, Key, Shield } from "lucide-react" import { Check, DollarSign, Key, Shield } from "lucide-react"
import { downloadAndNavigate } from "@/lib/utils"
export default function Pricing() { export default function Pricing() {
return ( return (
@@ -122,7 +125,7 @@ export default function Pricing() {
</div> </div>
</div> </div>
<Button size="lg" className="bg-green-600 hover:bg-green-700 text-lg px-8 py-4"> <Button size="lg" className="bg-green-600 hover:bg-green-700 text-lg px-8 py-4" onClick={downloadAndNavigate}>
Download Free Now Download Free Now
</Button> </Button>
+123 -93
View File
@@ -1,7 +1,25 @@
import { Button } from "@/components/ui/button" "use client";
import { Card, CardContent } from "@/components/ui/card"
import { Download, AlertTriangle, Calendar, DollarSign, Shield, MessageSquare } from "lucide-react" import { Button } from "@/components/ui/button";
import Header from "../components/header" import { Card, CardContent } from "@/components/ui/card";
import {
Download,
AlertTriangle,
Calendar,
DollarSign,
Shield,
MessageSquare,
} from "lucide-react";
import Header from "../components/header";
import { downloadAndNavigate } from "@/lib/utils";
function triggerDownload() {
// Just trigger the download without navigation since we're already on the download page
window.open(
"https://github.com/owengretzinger/meetingnotes/releases/latest/download/Meetingnotes.dmg",
"_blank"
);
}
export default function DownloadPage() { export default function DownloadPage() {
return ( return (
@@ -9,20 +27,66 @@ export default function DownloadPage() {
<Header /> <Header />
<div className="container mx-auto px-4 py-20"> <div className="container mx-auto px-4 py-20">
<div className="max-w-4xl mx-auto"> <div className="max-w-4xl mx-auto">
<div className="grid md:grid-cols-3 gap-8 mb-8">
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card className="bg-gray-900/50 border-gray-800"> <Card className="bg-gray-900/50 border-gray-800">
<CardContent className="p-8"> <CardContent className="p-8">
<div className="flex items-center mb-4"> <div className="w-16 h-16 bg-yellow-500/20 rounded-full flex items-center justify-center mb-4">
<MessageSquare className="w-8 h-8 text-blue-400 mr-3" /> <svg
<h2 className="text-2xl font-bold">Let's Talk First</h2> className="w-8 h-8 text-yellow-400"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
</div> </div>
<h2 className="text-2xl font-bold mb-4">Star on GitHub</h2>
<p className="text-gray-300 mb-6"> <p className="text-gray-300 mb-6">
Right now I'm trying to gauge interest in Meetingnotes, so I would love to jump on a call to walk you Consider starring the project to show support and help others
through the set up and hear your thoughts on it. discover it.
</p> </p>
<Button size="lg" className="bg-blue-600 hover:bg-blue-700 w-full" asChild> <Button
<a href="https://cal.com/owengretzinger" target="_blank" rel="noopener noreferrer"> size="lg"
className="bg-yellow-600 hover:bg-yellow-700 w-full"
asChild
>
<a
href="https://github.com/owengretzinger/meetingnotes"
target="_blank"
rel="noopener noreferrer"
>
<svg
className="w-5 h-5 mr-2"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
</svg>
Star on GitHub
</a>
</Button>
</CardContent>
</Card>
<Card className="bg-gray-900/50 border-gray-800">
<CardContent className="p-8">
<div className="w-16 h-16 bg-blue-500/20 rounded-full flex items-center justify-center mb-4">
<Calendar className="w-8 h-8 text-blue-400" />
</div>
<h2 className="text-2xl font-bold mb-4">Book a Call</h2>
<p className="text-gray-300 mb-6">
I would love to walk you through the setup and hear your
thoughts.
</p>
<Button
size="lg"
className="bg-blue-600 hover:bg-blue-700 w-full"
asChild
>
<a
href="https://cal.com/owengretzinger"
target="_blank"
rel="noopener noreferrer"
>
<Calendar className="w-5 h-5 mr-2" /> <Calendar className="w-5 h-5 mr-2" />
Schedule a Call Schedule a Call
</a> </a>
@@ -32,108 +96,74 @@ export default function DownloadPage() {
<Card className="bg-gray-900/50 border-gray-800"> <Card className="bg-gray-900/50 border-gray-800">
<CardContent className="p-8"> <CardContent className="p-8">
<div className="flex items-center mb-4"> <div className="w-16 h-16 bg-green-500/20 rounded-full flex items-center justify-center mb-4">
<Download className="w-8 h-8 text-green-400 mr-3" /> <DollarSign className="w-8 h-8 text-green-400" />
<h2 className="text-2xl font-bold">Download Now</h2>
</div> </div>
<h2 className="text-2xl font-bold mb-4">Get API Key</h2>
<p className="text-gray-300 mb-6"> <p className="text-gray-300 mb-6">
Ready to try it out? Download the latest version of Meetingnotes for macOS (Requires macOS 14.0 or later). You'll need it to transcribe and generate enhanced notes.
(~$0.20/hour)
</p> </p>
<Button size="lg" className="bg-green-600 hover:bg-green-700 w-full" asChild> <Button
<a href="https://github.com/owengretzinger/meetingnotes/releases" target="_blank" rel="noopener noreferrer"> size="lg"
<Download className="w-5 h-5 mr-2" /> className="bg-green-600 hover:bg-green-700 w-full"
Download v1.0 asChild
>
<a
href="https://platform.openai.com/api-keys"
target="_blank"
rel="noopener noreferrer"
>
<DollarSign className="w-5 h-5 mr-2" />
Get API Key
</a> </a>
</Button> </Button>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
<Card className="bg-red-900/20 border-red-800/50 mb-12"> <p className="text-center text-gray-400 text-sm mb-12">
<CardContent className="p-8"> <a
<div className="flex items-start mb-4"> href="https://github.com/owengretzinger/meetingnotes/releases/latest/download/Meetingnotes.dmg"
<AlertTriangle className="w-8 h-8 text-red-400 mr-3 mt-1 flex-shrink-0" /> className="text-blue-400 hover:text-blue-300 underline transition-colors"
<div> >
<h2 className="text-2xl font-bold text-red-400 mb-2">Important Security Notice</h2> Download didn't start automatically?{" "}
<p className="text-gray-300 mb-4"> </a>
I haven't paid the Apple developer fee, which means that when you download the app you might have </p>
trouble opening it due to macOS flagging it for security issues.
</p>
<div className="bg-gray-900/50 rounded-lg p-4">
<h3 className="font-bold text-white mb-2">To open the app:</h3>
<ol className="text-gray-300 space-y-2 list-decimal list-inside">
<li>Right-click the app and select "Open"</li>
<li>Click through the security dialogue</li>
<li>Go to System Preferences → Privacy & Security</li>
<li>Scroll to the bottom and click "Open Anyway"</li>
</ol>
</div>
</div>
</div>
</CardContent>
</Card>
<div className="grid md:grid-cols-2 gap-8 mb-12">
<Card className="bg-gray-900/50 border-gray-800">
<CardContent className="p-8">
<div className="flex items-center mb-4">
<DollarSign className="w-8 h-8 text-yellow-400 mr-3" />
<h2 className="text-2xl font-bold">API Key Required</h2>
</div>
<p className="text-gray-300 mb-4">
You will need to grab an OpenAI API key to use the transcription and AI features.
</p>
<p className="text-sm text-gray-400 mb-6">
<strong>Expected cost:</strong> About $0.20/hour of meeting time
</p>
<Button variant="outline" size="lg" className="border-gray-600 text-gray-300 hover:bg-gray-800 w-full bg-transparent" asChild>
<a href="https://platform.openai.com/api-keys" target="_blank" rel="noopener noreferrer">
Get OpenAI API Key
</a>
</Button>
</CardContent>
</Card>
<Card className="bg-gray-900/50 border-gray-800">
<CardContent className="p-8">
<div className="flex items-center mb-4">
<Shield className="w-8 h-8 text-purple-400 mr-3" />
<h2 className="text-2xl font-bold">Privacy First</h2>
</div>
<p className="text-gray-300 mb-4">
Your API key and all meeting data stay on your device. Nothing is sent to our servers.
</p>
<ul className="text-sm text-gray-400 space-y-2">
<li>• API keys stored securely on-device</li>
<li>• Meeting recordings never leave your Mac</li>
<li>• No data collection or tracking</li>
<li>• Open source and transparent</li>
</ul>
</CardContent>
</Card>
</div>
<div className="text-center"> <div className="text-center">
<h2 className="text-3xl font-bold mb-4">Questions?</h2> <h2 className="text-3xl font-bold mb-4">Questions?</h2>
<p className="text-gray-300 mb-8"> <p className="text-gray-300 mb-8">
If you run into any issues or have questions about the setup, don't hesitate to reach out. If you run into any issues or have questions about the setup,
don't hesitate to reach out.
</p> </p>
<div className="flex flex-col sm:flex-row gap-4 justify-center"> <div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button size="lg" className="bg-blue-600 hover:bg-blue-700" asChild> <Button
<a href="https://cal.com/owengretzinger" target="_blank" rel="noopener noreferrer"> size="lg"
className="bg-blue-600 hover:bg-blue-700"
asChild
>
<a
href="https://cal.com/owengretzinger"
target="_blank"
rel="noopener noreferrer"
>
<Calendar className="w-5 h-5 mr-2" /> <Calendar className="w-5 h-5 mr-2" />
Schedule a Call Schedule a Call
</a> </a>
</Button> </Button>
<Button variant="outline" size="lg" className="border-gray-600 text-gray-300 hover:bg-gray-800 bg-transparent" asChild> <Button
<a href="mailto:[email protected]"> variant="outline"
Contact Me size="lg"
</a> className="border-gray-600 text-gray-300 hover:bg-gray-800 bg-transparent"
asChild
>
<a href="mailto:[email protected]">Contact Me</a>
</Button> </Button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
) );
} }
+8
View File
@@ -4,3 +4,11 @@ import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))
} }
export function downloadAndNavigate() {
// Open download URL in a new tab/window (this will trigger the download)
window.open('https://github.com/owengretzinger/meetingnotes/releases/latest/download/Meetingnotes.dmg', '_blank')
// Navigate to download page
window.location.href = '/download'
}
+1 -1
View File
@@ -30,7 +30,7 @@ RELEASES_DIR="$(pwd)/releases"
VERSION_DIR="${RELEASES_DIR}/v${VERSION}" VERSION_DIR="${RELEASES_DIR}/v${VERSION}"
mkdir -p "$VERSION_DIR" mkdir -p "$VERSION_DIR"
DMG_NAME="${APP_NAME}-${VERSION}.dmg" DMG_NAME="${APP_NAME}.dmg"
ZIP_NAME="${APP_NAME}-${VERSION}.zip" ZIP_NAME="${APP_NAME}-${VERSION}.zip"
# Absolute paths for the artifacts # Absolute paths for the artifacts
DMG_PATH="${VERSION_DIR}/${DMG_NAME}" DMG_PATH="${VERSION_DIR}/${DMG_NAME}"