diff --git a/apps/web/content/articles/why-we-burned-it-down.mdx b/apps/web/content/articles/why-we-burned-it-down.mdx new file mode 100644 index 0000000000..b995bd161a --- /dev/null +++ b/apps/web/content/articles/why-we-burned-it-down.mdx @@ -0,0 +1,229 @@ +--- +display_title: "Why We Burned It Down - John's POV (the chaos)" +meta_title: "Why We Burned It Down and Rebuilt Hyprnote From Scratch" +meta_description: "The messy truth about why we rewrote Hyprnote. No product philosophy, too many pivots, motion without progress—and how we finally got focused." +author: "John Jeong" +created: "2025-11-10" +coverImage: "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/blog/why-we-burned-it-down/cover.png" +--- + +### tl;dr +We didn't rewrite Hyprnote because it was smart strategy. +We rewrote it because what we built before was confused. +And the only way to fix it was to burn it down. + +### The Early Mess + +When Yujong and I started, we weren't working on Hyprnote. +We were trying to build an AI hardware toy that talks to kids. +But hardware was slow. Brutal. Not something two people could actually ship fast.. + +So we pivoted to software. + +I'd spent a lot of time in meetings, trying AI notetakers like Clovanote. +None of them captured nuance — the difference between "ok" and "OK!" matters when you're selling. +I wanted something stupidly simple, clean, fast. Something I'd actually use. +That was the spark for Hyprnote. + +### Shiny Tech. No Philosophy. + +Early on, we got distracted. +We picked Tauri because it felt cool. +We said "let's do local-first everything" because it sounded clever. +Transcription, LLM, cloud, BYOK, lifetime license… everything. + +We had no product philosophy. +No sharp segment. +Just vibes. + +People liked it at first. Open source folks said they'd use it over Granola just because it was open source. That gave us confidence… but also made us more confused. + +Local-first ≠ open source. +They're adjacent. Not the same. +And we never picked a lane. + +### YC Made It Worse + +Then Y Combinator happened. +And this is where we stopped thinking straight. + +Our group partners kept asking the right questions. Every single time. + +The question that we struggled the most was: + +> Who are your users? + +We said that we were focusing on enterprise — but we weren't sure. +We were still trying to figure out who we should be selling to: banks, defense, military, or whatever. + +They weren't the problem. We were. +Instead of slowing down to reflect, we tried to answer immediately. +We mistook speed for clarity. + +As James from Pioneer Fund told me: + +> Don't mistake motion for progress. + +He said it's written on his son's door. +We fell into that trap hard. + +We were moving fast. +Just in 12 different directions at once. +Vectors everywhere. + +### Too Much Logic. Too Much Talking. Too Little Clarity. + +We debated everything. Local vs cloud. Enterprise vs prosumer. Bottom-up vs top-down. +We tried to _logic_ our way into clarity. +You can't. + +Add hiring on top of that and it got worse. +Too many opinions. Too much communication overhead. +Alignment took forever. We moved slower, not faster. + +Honestly, YC was a wake-up call for me on hiring. +I'm not hiring again for a while. Just me and Yujong now — and we're moving faster than ever. + +### Investors Pushed One Way. My Gut Said Another. + +Early angels pushed the "local-first enterprise" angle. +"Regulated industries love data sovereignty." +And yeah, they do. But enterprise ≠ local-first. +They care about compliance, SOC2, consent, legal — we weren't ready for any of that. + +And the whole time, my gut said: +Build for people like you. +Busy individuals who live in meetings and want a tool that just _works_. + +I ignored my gut for months. That was on me. + +### The Product Rotted + +Because we never picked a path, we tried to build everything: + +- Local speech-to-text +- Local LLM +- Bring-your-own-endpoints +- owhisper +- Admin server +- Hybrid setups no one fully understood + +It became this big, over-engineered pile of "potential" that felt bad to use. +The codebase was tangled. The design was directionless. +I didn't even like opening our own app anymore. + +### Motion Isn't Progress + +For months, I was obsessed with motion. +Metrics. Launches. Growth hacks. +But everything starts and ends with product. + +I've seen the hype cycles in the valley. They all crash. +What's left after the hype is gone is the product. Nothing else. + +### The MVP Isn't What It Used to Be + +Around this time, I read something from the Linear blog that really clicked. + +The definition of "MVP" is different now. +Back in the lean startup era, an MVP could be a scrappy little wedge — barely working. +Not anymore. + +Users expect more. +Products have matured. +An MVP today still has to _feel complete_. +It can be scrappy under the hood, but it can't be crappy in the hands. + +And if your wedge is _design_, like us, you don't even get that scrappy margin. +You have to nail it. + +So we stopped trying to play the "ship crap fast" game. +We're going to build something tight, sharp, and clean — and ship it with confidence. + +### Ferrari vs Porsche + +I've been thinking a lot about differentiators. Honestly? I don't have a perfect answer. +Granola exists. They're great. They're Porsche. + +We want to be Ferrari. + +Both are sports cars. Both fast. Both in the same league. +But Ferrari is hand-crafted, opinionated, focused on a specific segment. +Porsche is more accessible, more mass. + +We're not trying to win everyone. +We're trying to dominate a niche that _really cares_. + +We'll filter people through a waitlist. We'll charge from the start. +And if people don't like that, that's fine. We're not for everyone. + +### Just Focus + +I've stopped caring about what others are building. +Competitors matter long term, sure. +But right now, it's us, our users, and the product. + +### Burn It Down. Build It Better. + +Fixing the old app would've taken longer than rewriting it. +And honestly — it didn't deserve to be fixed. + +So we burned it down. +We rebuilt everything from scratch. +New app. Clean architecture. Sharper focus. + +This time: + +- We're building for people like us. +- We're opinionated. +- We're not mistaking speed for progress. +- We're not shipping crap just to "look busy." +- We're going Ferrari mode. + +This isn't a polished comeback story. +It's just us finally admitting what we messed up. +And getting back to building a product we actually believe in. + + + +
+

Hyprnote

+ +
+
+
+ John Jeong +
+ John Jeong +
+
+
+ Yujong Lee +
+ Yujong Lee +
+
+
+ + + + + diff --git a/apps/web/src/routes/_view/download/index.tsx b/apps/web/src/routes/_view/download/index.tsx index 6e50914d69..afd202113a 100644 --- a/apps/web/src/routes/_view/download/index.tsx +++ b/apps/web/src/routes/_view/download/index.tsx @@ -16,86 +16,90 @@ function Component() { className="bg-linear-to-b from-white via-blue-50/20 to-white min-h-screen" style={{ backgroundImage: "url(/patterns/dots.svg)" }} > -
-
- Mac (Apple Silicon) features on-device speech-to-text. Other platforms coming soon without on-device - processing. +
+
+ + Mac (Apple Silicon) features on-device speech-to-text. Other platforms coming soon without on-device + processing. +
-
-
-
-
-

- Download Hyprnote -

-

- Choose your platform to get started with Hyprnote -

-
+
+
+
+

+ Download Hyprnote +

+

+ Choose your platform to get started with Hyprnote +

+
-
-

- Desktop -

-
- - - - +
+

+ Desktop +

+
+ + + + +
-
-
-

- Mobile -

-
- - +
+

+ Mobile +

+
+ + +
-
-
- - - - +
+ + + + +
); @@ -134,7 +138,11 @@ function DownloadCard({ : ( diff --git a/apps/web/src/routes/_view/index.tsx b/apps/web/src/routes/_view/index.tsx index d13898108b..c9f68bf613 100644 --- a/apps/web/src/routes/_view/index.tsx +++ b/apps/web/src/routes/_view/index.tsx @@ -30,18 +30,21 @@ const mainFeatures = [ title: "Transcript", description: "Realtime transcript and speaker identification", image: "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/transcript.jpg", + link: "/product/ai-notetaking#transcription", }, { icon: "mdi:file-document-outline", title: "Summary", description: "Create customized summaries with templates for various formats", image: "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/summary.jpg", + link: "/product/ai-notetaking#summaries", }, { icon: "mdi:chat-outline", title: "Chat", description: "Get context-aware answers in realtime, even from past meetings", image: "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/chat.jpg", + link: "/product/ai-assistant", }, { icon: "mdi:window-restore", @@ -63,6 +66,7 @@ const detailsFeatures = [ title: "Notion-like Editor", description: "Full markdown support with distraction-free writing", image: "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/editor.jpg", + link: "/product/ai-notetaking#editor", }, { icon: "mdi:account-multiple-outline", @@ -81,6 +85,7 @@ const detailsFeatures = [ title: "Upload Audio", description: "Import audio files or transcripts to convert into notes", comingSoon: true, + link: "/product/ai-notetaking#upload", }, { icon: "mdi:bookshelf", @@ -1157,25 +1162,28 @@ function FeaturesDesktopGrid() { alt={`${feature.title} feature`} className="w-full h-full object-contain" /> -
- -
+ + Learn more + + + )} ) : ( @@ -1444,27 +1452,30 @@ function DetailsDesktopView() { alt={`${selectedFeature.title} feature`} className="w-full h-full object-contain" /> -
- -
+ + Learn more + + + )} ) : ( diff --git a/apps/web/src/routes/_view/pricing.tsx b/apps/web/src/routes/_view/pricing.tsx index 7035ca94b8..83045393e1 100644 --- a/apps/web/src/routes/_view/pricing.tsx +++ b/apps/web/src/routes/_view/pricing.tsx @@ -13,6 +13,7 @@ export const Route = createFileRoute("/_view/pricing")({ interface PricingPlan { name: string; price: { monthly: number; yearly: number } | null; + originalPrice?: { monthly: number; yearly: number }; description: string; popular?: boolean; features: Array<{ @@ -30,7 +31,7 @@ const pricingPlans: PricingPlan[] = [ description: "Fully functional with your own API keys. Perfect for individuals who want complete control.", features: [ { label: "Local Transcription", included: true }, - { label: "Speaker Identification", included: true }, + { label: "Speaker Identification", included: true, comingSoon: true }, { label: "Bring Your Own Key (STT & LLM)", included: true }, { label: "Basic Sharing (Copy, PDF)", included: true }, { label: "All Data Local", included: true }, @@ -45,8 +46,12 @@ const pricingPlans: PricingPlan[] = [ { name: "Pro", price: { - monthly: 35, - yearly: 295, + monthly: 8, + yearly: 59, + }, + originalPrice: { + monthly: 20, + yearly: 169, }, description: "No API keys needed. Get cloud services, advanced sharing, and team features out of the box.", popular: true, @@ -89,20 +94,18 @@ function Component() { function TeamPricingBanner() { return ( - -
- Have questions about teams pricing? - Contact us -
- +
+ + Early Bird Discount: Get 60% off as we launch our new version and help with migration + +
); } @@ -157,14 +160,24 @@ function PricingCard({ plan }: { plan: PricingPlan }) { {plan.price ? (
-
+
${plan.price.monthly} - /seat/month + {plan.originalPrice && ( + + ${plan.originalPrice.monthly} + + )} + /seat/month
- or ${plan.price.yearly}/seat/year (save 30%) + or ${plan.price.yearly}/seat/year{" "} + {plan.originalPrice && ( + + ${plan.originalPrice.yearly} + + )} (save 65%)
) diff --git a/apps/web/src/routes/_view/product/ai-assistant.tsx b/apps/web/src/routes/_view/product/ai-assistant.tsx index f70723d260..7c2144482e 100644 --- a/apps/web/src/routes/_view/product/ai-assistant.tsx +++ b/apps/web/src/routes/_view/product/ai-assistant.tsx @@ -3,6 +3,8 @@ import { cn } from "@hypr/utils"; import { Icon } from "@iconify-icon/react"; import { createFileRoute, Link } from "@tanstack/react-router"; +import { SlashSeparator } from "@/components/slash-separator"; + export const Route = createFileRoute("/_view/product/ai-assistant")({ component: Component, head: () => ({ @@ -24,18 +26,56 @@ function Component() { style={{ backgroundImage: "url(/patterns/dots.svg)" }} >
-
-
-

- AI assistant for your -
- conversations +
+
+

+ AI assistant for your conversations

-

- Ask questions and get instant answers from all your recorded meetings and notes. Powered by local AI that - understands your entire conversation history. +

+ Ask questions and get instant answers from your meetings and notes using local AI

+
+
+ + + +
+
+
+
+

Natural Language

+

+ Ask questions in plain English, get accurate answers instantly. +

+
+
+

Context-Aware

+

+ AI understands your full conversation history for better answers. +

+
+
+

Private & Local

+

+ All queries processed on your device, nothing sent to cloud. +

+
+
+
+ +

Ask anything

@@ -111,6 +151,8 @@ function Component() {
+ +

How your AI assistant works @@ -146,6 +188,8 @@ function Component() {

+ +

Powerful use cases

@@ -180,6 +224,8 @@ function Component() {
+ +
@@ -192,38 +238,66 @@ function Component() {

+
-
-

+ + +
+
+
+ Hyprnote +
+

Start using your AI assistant

-

- Get instant answers from all your meeting notes with Hyprnote's AI assistant. +

+ Get instant answers from all your meeting notes with Hyprnote's AI assistant

-
+
Download for free + + + Learn about Notepad
-
-

+ + ); diff --git a/apps/web/src/routes/_view/product/ai-notetaking.tsx b/apps/web/src/routes/_view/product/ai-notetaking.tsx index 72bdc0a3a8..b453f30444 100644 --- a/apps/web/src/routes/_view/product/ai-notetaking.tsx +++ b/apps/web/src/routes/_view/product/ai-notetaking.tsx @@ -1,7 +1,11 @@ import { cn } from "@hypr/utils"; + import { Icon } from "@iconify-icon/react"; -import { createFileRoute, Link } from "@tanstack/react-router"; -import { allTemplates } from "content-collections"; +import { createFileRoute } from "@tanstack/react-router"; +import { useEffect, useState } from "react"; + +import { MockWindow } from "@/components/mock-window"; +import { SlashSeparator } from "@/components/slash-separator"; export const Route = createFileRoute("/_view/product/ai-notetaking")({ component: Component, @@ -32,429 +36,1090 @@ function Component() { style={{ backgroundImage: "url(/patterns/dots.svg)" }} >
-
-
-

- AI notetaking that -
- captures everything -

-

- Record meetings in real-time or upload audio files for transcription. Get instant AI-generated summaries - with customizable templates. Works with any video conferencing tool. + + + + + + + + + + + + + + + +

+
+ ); +} + +function HeroSection() { + return ( +
+
+
+

+ AI notetaking that captures everything +

+

+ Record meetings or upload audio files to get instant AI transcriptions and customizable summaries +

+
+ + Download for free + +
+
+
+
+ AI notetaking in action +
+
+ ); +} + +function EditorSection() { + return ( +
+
+
+
+

Notion-like editor with markdown support

+

+ Write and organize your notes with a powerful, intuitive editor that supports full markdown syntax.

- - -
-

Complete notetaking workflow

-
-
-
- 1 -
-

Record or upload

-

- Capture live audio or upload existing files -

-
-
-
- 2 -
-

AI transcription

-

- Local AI converts speech to text with high accuracy -

+
    +
  • + + Full markdown syntax support for quick formatting +
  • +
  • + + Clean, distraction-free writing experience +
  • +
  • + + Rich text editing with familiar keyboard shortcuts +
  • +
+
+
+
+ +
+ +
+
+
+
+ +
+
+

Notion-like editor with markdown support

+

+ Write and organize your notes with a powerful, intuitive editor that supports full markdown syntax. +

+
    +
  • + + Full markdown syntax support for quick formatting +
  • +
  • + + Clean, distraction-free writing experience +
  • +
  • + + Rich text editing with familiar keyboard shortcuts +
  • +
+
+
+
+
+
+
+
+
-
-
- 3 +
+
+ +
+
+
+
+
+ ); +} + +function AudioTranscriptionDemo() { + const [progress, setProgress] = useState(0); + + // Words with their position along the timeline (0 to 1) + // Positioning accounts for approximate text width to prevent overlap + const words = [ + { position: 0.02, text: "Welcome" }, // ~7 chars + { position: 0.15, text: "to" }, // ~2 chars + { position: 0.20, text: "today's" }, // ~7 chars + { position: 0.33, text: "meeting" }, // ~7 chars + { position: 0.48, text: "Let's" }, // ~5 chars + { position: 0.59, text: "discuss" }, // ~7 chars + { position: 0.73, text: "the" }, // ~3 chars + { position: 0.79, text: "Q4" }, // ~2 chars + { position: 0.86, text: "roadmap" }, // ~7 chars + ]; + + // Generate static audio bars with varying heights (memoized to prevent regeneration) + const audioBarHeights = useState(() => { + const audioBarCount = 60; + return Array.from({ length: audioBarCount }, () => { + // Random heights between 20% and 100% + return Math.random() * 0.8 + 0.2; + }); + })[0]; + + useEffect(() => { + const duration = 8000; // 8 seconds for full cycle + const startTime = Date.now(); + + const animate = () => { + const elapsed = Date.now() - startTime; + const newProgress = (elapsed % duration) / duration; + + setProgress(newProgress); + + requestAnimationFrame(animate); + }; + + const animationId = requestAnimationFrame(animate); + return () => cancelAnimationFrame(animationId); + }, []); + + return ( +
+ {/* Audio bars - taking up more vertical space */} +
+ {audioBarHeights.map((height, i) => { + const isTranscribed = i / audioBarHeights.length <= progress; + return ( +
+ ); + })} +
+ + {/* Subtitle display - words appear along timeline */} +
+ {words.map((word, i) => { + const isVisible = progress >= word.position; + return ( + + {word.text} + + ); + })} +
+
+ ); +} + +function AnimatedMarkdownDemo({ isMobile = false }: { isMobile?: boolean }) { + const [completedLines, setCompletedLines] = useState([]); + const [currentLineIndex, setCurrentLineIndex] = useState(0); + const [typingText, setTypingText] = useState(""); + const [isTransformed, setIsTransformed] = useState(false); + + const lines = [ + { text: "# Meeting Notes", type: "heading" as const }, + { text: "- Product roadmap review", type: "bullet" as const }, + { text: "- Q4 marketing strategy", type: "bullet" as const }, + { text: "- Budget allocation", type: "bullet" as const }, + { text: "**Decision:** Launch campaign by end of month", type: "bold" as const }, + ]; + + useEffect(() => { + if (currentLineIndex >= lines.length) { + // Reset animation after a pause + const timeout = setTimeout(() => { + setCompletedLines([]); + setCurrentLineIndex(0); + setTypingText(""); + setIsTransformed(false); + }, 2000); + return () => clearTimeout(timeout); + } + + const currentLine = lines[currentLineIndex]; + let charIndex = 0; + let timeout: NodeJS.Timeout; + + const typeCharacter = () => { + if (charIndex < currentLine.text.length) { + const newText = currentLine.text.slice(0, charIndex + 1); + setTypingText(newText); + charIndex++; + + // Check if we just typed the trigger characters + const shouldTransform = (currentLine.type === "heading" && newText === "# ") + || (currentLine.type === "bullet" && newText === "- ") + || (currentLine.type === "bold" && newText.match(/\*\*[^*]+\*\*/)); + + if (shouldTransform) { + setIsTransformed(true); + } + + timeout = setTimeout(typeCharacter, 60); + } else { + // Finished typing this line + timeout = setTimeout(() => { + // Add the completed line + const completedElement = renderCompletedLine(currentLine, isMobile); + if (completedElement) { + setCompletedLines((prev) => [...prev, completedElement]); + } + + // Reset for next line + setTypingText(""); + setIsTransformed(false); + setCurrentLineIndex((prev) => prev + 1); + }, 800); + } + }; + + typeCharacter(); + + return () => clearTimeout(timeout); + }, [currentLineIndex, isMobile]); + + const renderCompletedLine = (line: typeof lines[number], mobile: boolean) => { + const key = `completed-${currentLineIndex}`; + + if (line.type === "heading") { + const text = line.text.replace("# ", ""); + return ( +

+ {text} +

+ ); + } + + if (line.type === "bullet") { + const text = line.text.replace("- ", ""); + return ( +
    +
  • {text}
  • +
+ ); + } + + if (line.type === "bold") { + const parts = line.text.split(/(\*\*.*?\*\*)/g); + return ( +

+ {parts.map((part, i) => { + if (part.startsWith("**") && part.endsWith("**")) { + return {part.slice(2, -2)}; + } + return part; + })} +

+ ); + } + + return null; + }; + + const renderCurrentLine = () => { + const currentLine = lines[currentLineIndex]; + + if (!currentLine) { + return null; + } + + if (currentLine.type === "heading" && isTransformed) { + const displayText = typingText.slice(2); // Remove "# " + return ( +

+ {displayText} + | +

+ ); + } + + if (currentLine.type === "bullet" && isTransformed) { + const displayText = typingText.slice(2); // Remove "- " + return ( +
    +
  • + {displayText} + | +
  • +
+ ); + } + + if (currentLine.type === "bold" && isTransformed) { + const parts = typingText.split(/(\*\*.*?\*\*)/g); + return ( +

+ {parts.map((part, i) => { + if (part.startsWith("**") && part.endsWith("**")) { + return {part.slice(2, -2)}; + } + return part; + })} + | +

+ ); + } + + // Show raw text before transformation + return ( +
+ {typingText} + | +
+ ); + }; + + return ( +
+ {completedLines} + {currentLineIndex < lines.length && renderCurrentLine()} +
+ ); +} + +function TranscriptionSection() { + return ( +
+
+

Transcription

+

+ From live meetings to recorded audio, Hyprnote can transcribe it all. +

+
+ +
+
+
+
+
+
+ +

Fully on-device

-

Smart summaries

-

- Generate summaries, action items, and insights -

-
-
-
- 4 +
+ Apple Silicon only
-

Search & share

-

- Find information instantly and export notes -

+

+ For Apple Silicon Macs, transcription happens entirely on your device. Fast, private, and no internet + required. +

-
- -
-

Two ways to capture audio

-
-
- -

Real-time recording

-

- Hit record before your meeting starts. Capture both your microphone and system audio simultaneously as - the conversation happens. -

-
    -
  • - - Live meeting capture -
  • -
  • - - Dual audio recording (mic + system) -
  • -
  • - - Works with Zoom, Meet, Teams, Slack, Discord -
  • -
  • - - No bots joining your meetings -
  • -
-
-
- -

Upload audio files

-

- Already have a recording? Drag and drop audio files to get transcripts and AI-generated summaries for - past conversations. -

-
    -
  • - - Process existing recordings -
  • -
  • - - Support for MP3, M4A, WAV, and more -
  • -
  • - - Batch upload multiple files -
  • -
  • - - Process interviews, podcasts, lectures -
  • -
-
+
+ On-device transcription
-
- -
-

- Intelligent AI summaries -

-

- Hyprnote's AI automatically extracts key points, decisions, and action items from your conversations. No - more manual note-taking or post-meeting cleanup. -

-
-
- -

Key points

-

- AI identifies the most important topics, insights, and takeaways from each conversation. -

+
+
+
+
+ +

Upload files

-
- -

Action items

-

- Automatically extracts tasks, follow-ups, and commitments made during the meeting. -

+

+ Upload audio files (M4A, MP3, WAV) or existing transcripts (VTT, TXT) to get AI summaries and insights. +

+
+
+ +
+
+
+ +
+
+
+
+ Apple Silicon only
-
- -

Decisions made

-

- Captures important decisions, agreements, and conclusions reached during discussions. -

+
+ +

Fully on-device

-
- -

Next steps

-

- Identifies planned actions, timelines, and what needs to happen after the meeting. -

+

+ For Apple Silicon Macs, transcription happens entirely on your device. Fast, private, and no internet + required. +

+
+
+ On-device transcription +
+
+
+
+
+ +

Upload files

+

+ Upload audio files (M4A, MP3, WAV) or existing transcripts (VTT, TXT) to get AI summaries and insights. +

-
- -
-

Customizable with templates

-

- Use templates to control how AI structures your summaries. Choose from our library of{" "} - {allTemplates.length} templates or create your own custom format for different meeting types. -

-
- {allTemplates.slice(0, 6).map((template) => )} +
+
-
- - Browse all {allTemplates.length} templates - - +
+
+
+ + ); +} + +function SummariesSection() { + const [typedText1, setTypedText1] = useState(""); + const [typedText2, setTypedText2] = useState(""); + const [enhancedLines, setEnhancedLines] = useState(0); + + const text1 = "metrisc w/ john"; + const text2 = "stakehlder mtg"; + + useEffect(() => { + const runAnimation = () => { + setTypedText1(""); + setTypedText2(""); + setEnhancedLines(0); + + let currentIndex1 = 0; + setTimeout(() => { + const interval1 = setInterval(() => { + if (currentIndex1 < text1.length) { + setTypedText1(text1.slice(0, currentIndex1 + 1)); + currentIndex1++; + } else { + clearInterval(interval1); + + let currentIndex2 = 0; + const interval2 = setInterval(() => { + if (currentIndex2 < text2.length) { + setTypedText2(text2.slice(0, currentIndex2 + 1)); + currentIndex2++; + } else { + clearInterval(interval2); + + setTimeout(() => { + setEnhancedLines(1); + setTimeout(() => { + setEnhancedLines(2); + setTimeout(() => { + setEnhancedLines(3); + setTimeout(() => { + setEnhancedLines(4); + setTimeout(() => runAnimation(), 1000); + }, 800); + }, 800); + }, 800); + }, 500); + } + }, 50); + } + }, 50); + }, 500); + }; + + runAnimation(); + }, []); + + return ( +
+
+

AI summaries

+

+ Get intelligent summaries after your meeting ends. Hyprnote combines your notes with transcripts to create + perfect summaries. +

+
+
+
+
+
+

+ While you take notes,{" "} + Hyprnote listens and keeps track of everything that happens during the meeting. +

-
- -
-

- Create custom templates -

-

- Need a specific format? Create your own custom templates to match your unique workflow and meeting - structure. -

-
-
- -
-

Define sections

-

- Specify what sections you want in your notes -

+
+ +
+
ui update - mobile
+
api
+
new dash - urgent
+
a/b test next wk
+
+ {typedText1} + {typedText1 && typedText1.length < text1.length && |} +
+
+ {typedText2} + {typedText2 && typedText2.length < text2.length && |} +
-
-
- -
-

Add instructions

-

- Tell AI what to look for in each section -

-
-
-
- -
-

Reuse anytime

-

- Save and use your template for future meetings -

+ +
+
+ +
+
+

+ After the meeting is over,{" "} + Hyprnote combines your notes with transcripts to create a perfect summary. +

+
+
+ +
+
+

= 1 ? "opacity-100" : "opacity-0", + )} + > + Mobile UI Update and API Adjustments +

+
    +
  • = 1 ? "opacity-100" : "opacity-0", + )} + > + Sarah presented the new mobile UI update, which includes a streamlined navigation bar and + improved button placements for better accessibility. +
  • +
  • = 2 ? "opacity-100" : "opacity-0", + )} + > + Ben confirmed that API adjustments are needed to support dynamic UI changes, particularly for + fetching personalized user data more efficiently. +
  • +
  • = 3 ? "opacity-100" : "opacity-0", + )} + > + The UI update will be implemented in phases, starting with core navigation improvements. Ben + will ensure API modifications are completed before development begins. +
  • +
+
+
+

= 4 ? "opacity-100" : "opacity-0", + )} + > + New Dashboard – Urgent Priority +

+
    +
  • = 4 ? "opacity-100" : "opacity-0", + )} + > + Alice emphasized that the new analytics dashboard must be prioritized due to increasing + stakeholder demand. +
  • +
  • = 5 ? "opacity-100" : "opacity-0", + )} + > + The new dashboard will feature real-time user engagement metrics and a customizable reporting + system. +
  • +
+
-
+ +
+
+
+ +
+
+
+

+ While you take notes,{" "} + Hyprnote listens and keeps track of everything that happens during the meeting. +

-
- -
-

Powerful features

-
-
- -
-

Speaker identification

-

- Automatically detect and label different speakers in your conversations. -

+
+
+
+
+
+
+
+
-
-
- -
-

Timestamp precision

-

- Jump to exact moments in your recording with accurate timestamps. -

+
+
ui update - mobile
+
api
+
new dash - urgent
+
a/b test next wk
+
+ {typedText1} + {typedText1 && typedText1.length < text1.length && |} +
+
+ {typedText2} + {typedText2 && typedText2.length < text2.length && |} +
-
- -
-

Multi-language support

-

- Transcribe conversations in 100+ languages with local AI models. -

+
+
+ +
+
+

+ After the meeting is over,{" "} + Hyprnote combines your notes with transcripts to create a perfect summary. +

+
+
+
+
+
+
+
+
+
-
-
- -
-

Export flexibility

-

- Export transcripts and summaries in TXT, MD, PDF, or DOCX formats. -

+
+
+

Mobile UI Update and API Adjustments

+
    +
  • = 1 ? "opacity-100" : "opacity-0", + )} + > + Sarah presented the new mobile UI update, which includes a streamlined navigation bar and + improved button placements for better accessibility. +
  • +
  • = 2 ? "opacity-100" : "opacity-0", + )} + > + Ben confirmed that API adjustments are needed to support dynamic UI changes, particularly for + fetching personalized user data more efficiently. +
  • +
  • = 3 ? "opacity-100" : "opacity-0", + )} + > + The UI update will be implemented in phases, starting with core navigation improvements. Ben + will ensure API modifications are completed before development begins. +
  • +
+
+
+

New Dashboard – Urgent Priority

+
    +
  • = 4 ? "opacity-100" : "opacity-0", + )} + > + Alice emphasized that the new analytics dashboard must be prioritized due to increasing + stakeholder demand. +
  • +
  • = 5 ? "opacity-100" : "opacity-0", + )} + > + The new dashboard will feature real-time user engagement metrics and a customizable reporting + system. +
  • +
+
-
- -
-

Perfect for every use case

-
- - - - - - - - - +
+ + +
+ ); +} + +function SearchSection() { + return ( + - -
-

- The complete AI notetaking solution -

-

- From live meetings to archived recordings, handle all your audio transcription and AI summary needs with - one powerful tool. -

-
- - Download for free - - - Learn about Local AI - +
+ +

What

+

+ Find by keywords, topics, or content +

-
+
+ +

When

+

+ Filter by date, time, or range +

+
+ + - +
+
+
+ Coming Soon +
+

Advanced search view

+

+ Build complex queries with boolean operators, date ranges, and custom filters for powerful search + capabilities. +

+
+
+ ); } -function UseCase({ - icon, - title, - description, -}: { - icon: string; - title: string; - description: string; -}) { +function SharingSection() { return ( -
- -

{title}

-

{description}

+
+
+
+ Coming Soon +
+

Share notes

+

+ Collaborate seamlessly by sharing meeting notes, transcripts, and summaries. +

+
+
+ +

Public sharing

+

+ Generate shareable links for easy distribution to anyone. +

+
+
+ +

Export formats

+

+ Export as PDF, Markdown, or plain text for maximum flexibility. +

+
+
+ +

Team sharing

+

+ Share with team members with expiration dates, watermarks, and access controls for Enterprise. +

+
+
+
+
+ ); +} + +function FloatingPanelSection() { + return ( +
+ + +
+ ); +} + +function FloatingPanelHeader() { + return ( +
+
+ Coming Soon +
+

Floating panel for meetings

+

+ A compact overlay that stays on top during meetings but won't show when you share your screen. +

); } -function getIconForTemplate(title: string): string { - const iconMap: Record = { - "Daily Standup": "mdi:run-fast", - "Sprint Planning": "mdi:calendar-star", - "Sprint Retrospective": "mdi:mirror", - "Product Roadmap Review": "mdi:road-variant", - "Customer Discovery Interview": "mdi:account-search", - "Sales Discovery Call": "mdi:phone", - "Technical Design Review": "mdi:draw", - "Executive Briefing": "mdi:tie", - "Board Meeting": "mdi:office-building", - "Performance Review": "mdi:chart-line", - "Client Kickoff Meeting": "mdi:rocket-launch", - "Brainstorming Session": "mdi:lightbulb-on", - "Incident Postmortem": "mdi:alert-circle", - "Lecture Notes": "mdi:school", - "Investor Pitch Meeting": "mdi:cash-multiple", - "1:1 Meeting": "mdi:account-multiple", - "Project Kickoff": "mdi:flag", - }; - return iconMap[title] || "mdi:file-document"; +function FloatingPanelContent() { + return ( +
+ + +
+ ); } -function TemplateCard({ template }: { template: (typeof allTemplates)[0] }) { - const icon = getIconForTemplate(template.title); +function FloatingPanelDesktop() { + const [selectedTab, setSelectedTab] = useState(0); + + const tabs = [ + { + title: "Compact Mode", + description: + "The default collapsed overlay that indicates the meeting is being listened to. Minimal and unobtrusive, staying out of your way.", + image: + "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/float-compact.jpg", + }, + { + title: "Memos", + description: + "Take quick notes during the meeting. Jot down important points, ideas, or reminders without losing focus on the conversation.", + image: "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/float-memos.jpg", + }, + { + title: "Transcript", + description: + "Watch the live transcript as the conversation unfolds in real-time, so you never miss what was said during the meeting.", + image: + "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/float-transcript.jpg", + }, + { + title: "Live Insights", + description: + "Get a rolling summary of the past 5 minutes with AI-powered suggestions. For sales calls, receive prompts for qualification questions and next steps.", + image: + "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/float-insights.jpg", + }, + { + title: "Chat", + description: + "Ask questions and get instant answers during the meeting. Query the transcript, get clarifications, or find specific information on the fly.", + image: "https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/hyprnote/float-chat.jpg", + }, + ]; return ( -
-
-
- -
-
-

- {template.title} -

-

{template.description}

+
+
+
+ {tabs.map((tab, index) => ( +
setSelectedTab(index)} + className={cn([ + "p-6 cursor-pointer transition-colors", + index < tabs.length - 1 && "border-b border-neutral-100", + selectedTab === index ? "bg-stone-50" : "hover:bg-neutral-50", + ])} + > +

+ {tab.title} – {tab.description} +

+
+ ))}
-
-
- Sections + +
+ {`${tabs[selectedTab].title} +
+
+ ); +} + +function FloatingPanelMobile() { + return ( +
+
+ Floating panel preview +
+
+

+ Compact Mode{" "} + – The default collapsed overlay that indicates the meeting is being listened to. Minimal and unobtrusive, + staying out of your way. +

+
+
+

+ Memos{" "} + – Take quick notes during the meeting. Jot down important points, ideas, or reminders without losing focus on + the conversation. +

+
+
+

+ Transcript{" "} + – Watch the live transcript as the conversation unfolds in real-time, so you never miss what was said during + the meeting. +

+
+
+

+ Live Insights{" "} + – Get a rolling summary of the past 5 minutes with AI-powered suggestions. For sales calls, receive prompts + for qualification questions and next steps. +

+
+
+

+ Chat{" "} + – Ask questions and get instant answers during the meeting. Query the transcript, get clarifications, or find + specific information on the fly. +

+
+
+ ); +} + +function CTASection() { + return ( +
+
+
+ Hyprnote
-
- {template.sections.slice(0, 3).map((section) => ( - + The complete AI notetaking solution + +

+ From live meetings to archived recordings, handle all your audio transcription and AI summary needs with one + powerful tool +

+
-
+
); } diff --git a/apps/web/src/routes/_view/product/api.tsx b/apps/web/src/routes/_view/product/api.tsx index c208e9db95..2948bb4165 100644 --- a/apps/web/src/routes/_view/product/api.tsx +++ b/apps/web/src/routes/_view/product/api.tsx @@ -1,3 +1,7 @@ +import { cn } from "@hypr/utils"; + +import { MockWindow } from "@/components/mock-window"; + import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/_view/product/api")({ @@ -16,24 +20,57 @@ export const Route = createFileRoute("/_view/product/api")({ function Component() { return (
-
-
-
-

+
+
+
+

Hyprnote API

-

+

Build custom applications and integrations with the Hyprnote API.

- + +
+ +
+
+ $ curl -X POST https://api.hyprnote.com/v1/notes \ +
+
+ -H "Authorization: Bearer YOUR_API_KEY" \ +
+
+ -H "Content-Type: application/json" \ +
+
+ -d '{"{"}"title": "Meeting Notes", "content": "..."{"}"}' +
+
+ {"{"} +
"id": "note_1a2b3c4d",
+
"title": "Meeting Notes",
+
"created_at": "2025-11-09T10:30:00Z"
+ {"}"} +
+
+
+
+ +
+ +
diff --git a/apps/web/src/routes/_view/product/bot.tsx b/apps/web/src/routes/_view/product/bot.tsx index 5c2925d394..e27b8556f5 100644 --- a/apps/web/src/routes/_view/product/bot.tsx +++ b/apps/web/src/routes/_view/product/bot.tsx @@ -1,4 +1,7 @@ +import { cn } from "@hypr/utils"; + import { createFileRoute } from "@tanstack/react-router"; +import { useCallback, useEffect, useRef, useState } from "react"; export const Route = createFileRoute("/_view/product/bot")({ component: Component, @@ -13,30 +16,163 @@ export const Route = createFileRoute("/_view/product/bot")({ }), }); +const DRAGGABLE_ICONS = [ + { left: "15%", top: "20%", rotate: "-8deg", size: 80 }, + { left: "75%", top: "25%", rotate: "5deg", size: 96 }, + { left: "20%", top: "65%", rotate: "12deg", size: 72 }, + { left: "80%", top: "70%", rotate: "-6deg", size: 88 }, +]; + function Component() { return (
-
-
-
-

+
+ {DRAGGABLE_ICONS.map((icon, idx) => ( + + ))} + +
+
+

Hyprnote Bot

-

+

An optional bot solution for teams that need centralized meeting recording and management.

- -

+
+ +
+

); } + +function DraggableIcon({ + initialPosition, + size, +}: { + initialPosition: { left: string; top: string; rotate: string }; + size: number; +}) { + const [dragStart, setDragStart] = useState<{ x: number; y: number } | null>(null); + const [position, setPosition] = useState({ x: 0, y: 0 }); + const [scale, setScale] = useState(1); + const iconRef = useRef(null); + + const calculatePosition = useCallback(() => { + if (typeof window === "undefined") { + return { x: 0, y: 0 }; + } + + const container = iconRef.current?.parentElement; + if (!container) { + return { x: 0, y: 0 }; + } + + const containerWidth = container.clientWidth; + const containerHeight = container.clientHeight; + + const x = (containerWidth * parseInt(initialPosition.left)) / 100 - size / 2; + const y = (containerHeight * parseInt(initialPosition.top)) / 100 - size / 2; + + return { x, y }; + }, [initialPosition, size]); + + useEffect(() => { + setPosition(calculatePosition()); + }, [calculatePosition]); + + useEffect(() => { + const handleResize = () => { + if (!dragStart) { + setPosition(calculatePosition()); + } + }; + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, [calculatePosition, dragStart]); + + const handlePointerDown = (e: React.PointerEvent) => { + if (!iconRef.current) { + return; + } + + iconRef.current.setPointerCapture(e.pointerId); + setScale(1.05); + setDragStart({ + x: e.clientX - position.x, + y: e.clientY - position.y, + }); + }; + + const handlePointerMove = (e: React.PointerEvent) => { + if (!dragStart || !iconRef.current?.hasPointerCapture(e.pointerId)) { + return; + } + + setPosition({ + x: e.clientX - dragStart.x, + y: e.clientY - dragStart.y, + }); + }; + + const handlePointerUp = (e: React.PointerEvent) => { + if (!iconRef.current) { + return; + } + + iconRef.current.releasePointerCapture(e.pointerId); + setScale(1); + setDragStart(null); + }; + + return ( +
+
+ Hyprnote +
+
+ ); +} diff --git a/apps/web/src/routes/_view/product/extensions.tsx b/apps/web/src/routes/_view/product/extensions.tsx index a1ff8a1bbb..6373a68d74 100644 --- a/apps/web/src/routes/_view/product/extensions.tsx +++ b/apps/web/src/routes/_view/product/extensions.tsx @@ -1,6 +1,5 @@ import { cn } from "@hypr/utils"; -import { Icon } from "@iconify-icon/react"; import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/_view/product/extensions")({ @@ -18,257 +17,56 @@ export const Route = createFileRoute("/_view/product/extensions")({ }); function Component() { - const integrations = [ - { name: "Slack", icon: "mdi:slack", description: "Share meeting notes to Slack channels" }, - { name: "Notion", icon: "simple-icons:notion", description: "Sync notes to your Notion workspace" }, - { name: "Google Calendar", icon: "mdi:google", description: "Auto-attach notes to calendar events" }, - { name: "Linear", icon: "simple-icons:linear", description: "Create issues from action items" }, - { name: "Jira", icon: "mdi:jira", description: "Sync tasks with Jira tickets" }, - { name: "Asana", icon: "simple-icons:asana", description: "Create tasks in Asana projects" }, - { name: "Trello", icon: "mdi:trello", description: "Add cards to Trello boards" }, - { name: "Microsoft Teams", icon: "mdi:microsoft-teams", description: "Share notes in Teams channels" }, - { name: "Salesforce", icon: "mdi:salesforce", description: "Log calls and notes to Salesforce" }, - { name: "HubSpot", icon: "simple-icons:hubspot", description: "Sync meeting notes to HubSpot CRM" }, - { name: "Zoom", icon: "mdi:video", description: "Auto-record Zoom meetings" }, - { name: "Google Meet", icon: "mdi:google-meet", description: "Join and record Google Meet calls" }, - ]; - - const apiFeatures = [ - { - icon: "mdi:code-json", - title: "RESTful API", - description: "Simple, REST-based API for easy integration", - }, - { - icon: "mdi:key", - title: "API Keys", - description: "Secure authentication with API keys", - }, - { - icon: "mdi:webhook", - title: "Webhooks", - description: "Real-time notifications for events", - }, - { - icon: "mdi:file-document", - title: "Documentation", - description: "Comprehensive guides and examples", - }, - { - icon: "mdi:code-braces", - title: "SDK Libraries", - description: "Official SDKs for popular languages", - }, - { - icon: "mdi:shield-check", - title: "Rate Limits", - description: "Fair usage policies with generous limits", - }, - ]; - - const useCases = [ - { - title: "Custom Workflows", - description: "Build automated workflows that integrate with your existing tools", - icon: "mdi:workflow", - }, - { - title: "Data Analysis", - description: "Extract meeting data for analytics and reporting", - icon: "mdi:chart-bar", - }, - { - title: "Internal Tools", - description: "Create custom internal apps powered by Hyprnote", - icon: "mdi:tools", - }, - { - title: "AI Applications", - description: "Build AI-powered features using meeting transcripts", - icon: "mdi:robot", - }, + const extensionAreas = [ + { label: "Custom Views", description: "Graph views, kanban boards, and more" }, + { label: "Themes", description: "Personalize your workspace" }, + { label: "Editor Plugins", description: "Enhanced editing capabilities" }, + { label: "Integrations", description: "Connect external tools" }, + { label: "Data Sources", description: "Import from anywhere" }, + { label: "Export Formats", description: "Share in any format" }, ]; return (
-
-
-
-
- Coming Soon -
-

- Extend Hyprnote with -
- integrations & API +
+
+
+

+ Build Beyond the Defaults

-

- Connect with your favorite tools through integrations, or build custom solutions with our developer API. - Coming soon. +

+ Extend Hyprnote with custom themes, plugins, and views. Build together with the community.

-

-
-

Built-in integrations

-

- Connect Hyprnote with the tools you use every day. No coding required. -

-
- {integrations.map((integration) => ( -
-
-
- -
-
-

- {integration.name} -

-

{integration.description}

-
-
-
- ))} -
-
- -
-

- Developer API -

-

- Build custom integrations, automate workflows, and create powerful applications on top of Hyprnote. -

-
- {apiFeatures.map((feature) => ( -
- -

- {feature.title} -

-

{feature.description}

-
- ))} -
-
- -
-

- What you can build with the API -

-
- {useCases.map((useCase) => ( -
-
- -
-
-

- {useCase.title} -

-

{useCase.description}

+
+
+ {extensionAreas.map((area) => ( +
+ {area.label}
-
- ))} + ))} +
-
-
-

API preview

-
-
-                {`// Example: Fetch recent meetings
-const response = await fetch('https://api.hyprnote.com/v1/meetings', {
-  headers: {
-    'Authorization': 'Bearer YOUR_API_KEY',
-    'Content-Type': 'application/json'
-  }
-});
-
-const meetings = await response.json();
-
-// Example: Get meeting transcript
-const meeting = await fetch(
-  'https://api.hyprnote.com/v1/meetings/abc123',
-  {
-    headers: { 'Authorization': 'Bearer YOUR_API_KEY' }
-  }
-).then(r => r.json());
-
-console.log(meeting.transcript);
-console.log(meeting.summary);
-console.log(meeting.action_items);`}
-              
-
-

- API design subject to change -

-
- -
-

- Request an integration or API access -

-

- We're actively developing integrations and our API. Share your use case and we'll reach out when we - launch. -

- -
- -
-

- Get notified when we launch -

-

- Be the first to know when extensions become available. -

- - Join waitlist - -
+
diff --git a/apps/web/src/routes/_view/product/local-ai.tsx b/apps/web/src/routes/_view/product/local-ai.tsx index 93d6b753cb..83d2df8e4b 100644 --- a/apps/web/src/routes/_view/product/local-ai.tsx +++ b/apps/web/src/routes/_view/product/local-ai.tsx @@ -1,4 +1,5 @@ import { cn } from "@hypr/utils"; + import { Icon } from "@iconify-icon/react"; import { createFileRoute, Link } from "@tanstack/react-router"; diff --git a/apps/web/src/routes/_view/product/mini-apps.tsx b/apps/web/src/routes/_view/product/mini-apps.tsx index 52425da61c..def263bc10 100644 --- a/apps/web/src/routes/_view/product/mini-apps.tsx +++ b/apps/web/src/routes/_view/product/mini-apps.tsx @@ -3,6 +3,8 @@ import { cn } from "@hypr/utils"; import { Icon } from "@iconify-icon/react"; import { createFileRoute } from "@tanstack/react-router"; +import { SlashSeparator } from "@/components/slash-separator"; + export const Route = createFileRoute("/_view/product/mini-apps")({ component: Component, head: () => ({ @@ -107,18 +109,57 @@ function Component() { style={{ backgroundImage: "url(/patterns/dots.svg)" }} >
-
-
-

- Built-in mini apps -
- for everything +
+
+

+ Built-in mini apps for everything

-

+

Hyprnote includes powerful mini apps for managing contacts, calendar, daily notes, and your personal knowledge base. Everything works together seamlessly.

+
+
+ + + +
+
+
+
+

All-in-One

+

+ Contacts, calendar, notes, and knowledge base in one place. +

+
+
+

Auto-Connected

+

+ Everything links automatically with your meetings and notes. +

+
+
+

Context-Rich

+

+ Full conversation history and context at your fingertips. +

+
+
+
+ +
@@ -156,6 +197,8 @@ function Component() {
+ +

How they work together @@ -191,6 +234,8 @@ function Component() {

+ +

Contacts

@@ -246,6 +291,8 @@ function Component() {

+ +

Calendar

@@ -290,9 +337,11 @@ function Component() {

+ +

Daily Notes

-
+
Coming Soon
@@ -317,9 +366,11 @@ function Component() {
+ +

Noteshelf

-
+
Coming Soon
@@ -358,6 +409,8 @@ function Component() {
+ +
@@ -370,27 +423,57 @@ function Component() {

+
-
-

+ + +
+
+
+ Hyprnote +
+

Get the complete experience

-

+

Download Hyprnote to start using contacts and calendar integration today. Daily notes and noteshelf coming - soon. + soon

- - Download for free - -
-

+ +
+
); diff --git a/apps/web/src/routes/_view/product/notepad.tsx b/apps/web/src/routes/_view/product/notepad.tsx index 534f66e890..4b9956a881 100644 --- a/apps/web/src/routes/_view/product/notepad.tsx +++ b/apps/web/src/routes/_view/product/notepad.tsx @@ -1,7 +1,10 @@ import { cn } from "@hypr/utils"; + import { Icon } from "@iconify-icon/react"; import { createFileRoute, Link } from "@tanstack/react-router"; +import { SlashSeparator } from "@/components/slash-separator"; + export const Route = createFileRoute("/_view/product/notepad")({ component: Component, head: () => ({ @@ -23,18 +26,54 @@ function Component() { style={{ backgroundImage: "url(/patterns/dots.svg)" }} >
-
-
-

- Your private notepad. -
- No bots. Local-first. +
+
+

+ Your private notepad. No bots. Local-first.

-

- Hyprnote is a desktop notepad that records meetings without bots and processes everything locally on your - device. Complete privacy, no cloud uploads, and no interruptions. +

+ Record meetings without bots and process everything locally for complete privacy

+
+
+ + + +
+
+
+
+

No Bots

+

+ Record directly from your device without intrusive meeting bots. +

+
+
+

Local-First

+

+ All AI processing happens on your device, nothing sent to cloud. +

+
+
+

Fully Private

+

+ Your conversations stay yours, complete data ownership. +

+
+
+

Why Hyprnote Notepad

@@ -94,6 +133,8 @@ function Component() {
+ +

Hyprnote vs. Bot-based services @@ -174,6 +215,8 @@ function Component() {

+ +

Built for privacy and compliance

@@ -220,6 +263,8 @@ function Component() {
+ +

Perfect for sensitive meetings

@@ -268,6 +313,8 @@ function Component() {
+ +

Powerful local AI capabilities

@@ -315,39 +362,67 @@ function Component() {
+
-
-

+ + +
+
+
+ Hyprnote +
+

The privacy-first notepad

-

+

Experience professional AI notetaking without bots, cloud uploads, or privacy compromises. Your data stays - yours. + yours

-
+
Download for free + + + Explore AI notetaking features
-
-

+ + ); diff --git a/apps/web/src/routes/_view/product/self-hosting.tsx b/apps/web/src/routes/_view/product/self-hosting.tsx index 6a71ed767f..72ae88de6b 100644 --- a/apps/web/src/routes/_view/product/self-hosting.tsx +++ b/apps/web/src/routes/_view/product/self-hosting.tsx @@ -1,3 +1,5 @@ +import { cn } from "@hypr/utils"; + import { createFileRoute } from "@tanstack/react-router"; export const Route = createFileRoute("/_view/product/self-hosting")({ @@ -17,26 +19,32 @@ export const Route = createFileRoute("/_view/product/self-hosting")({ function Component() { return (
-
-
-
-

+
+
+
+

Self-Hosting

-

+

Deploy Hyprnote on your own infrastructure for complete control, maximum security, and full data sovereignty.

- -

+
+ +
+
diff --git a/apps/web/src/routes/_view/product/workflows.tsx b/apps/web/src/routes/_view/product/workflows.tsx index 36d2000a7a..4e320a6755 100644 --- a/apps/web/src/routes/_view/product/workflows.tsx +++ b/apps/web/src/routes/_view/product/workflows.tsx @@ -1,7 +1,8 @@ import { cn } from "@hypr/utils"; -import { Icon } from "@iconify-icon/react"; +import { Image } from "@/components/image"; import { createFileRoute } from "@tanstack/react-router"; +import { useCallback, useEffect, useRef, useState } from "react"; export const Route = createFileRoute("/_view/product/workflows")({ component: Component, @@ -16,215 +17,399 @@ export const Route = createFileRoute("/_view/product/workflows")({ }), }); +const DRAGGABLE_ICONS = [ + { left: "15%", top: "25%", rotate: "-8deg", size: 76, flowDirection: "in", image: "slack.jpg" }, + { left: "78%", top: "28%", rotate: "5deg", size: 84, flowDirection: "out", image: "linear.jpg" }, + { left: "20%", top: "70%", rotate: "12deg", size: 72, flowDirection: "in", image: "notion.jpg" }, + { left: "82%", top: "72%", rotate: "-6deg", size: 80, flowDirection: "out", image: "salesforce.jpg" }, + { left: "10%", top: "48%", rotate: "3deg", size: 78, flowDirection: "out", image: "affinity.jpg" }, + { left: "88%", top: "50%", rotate: "-10deg", size: 74, flowDirection: "in", image: "attio.jpg" }, + { left: "35%", top: "22%", rotate: "8deg", size: 70, flowDirection: "in", image: "gcal.jpg" }, + { left: "60%", top: "20%", rotate: "-4deg", size: 82, flowDirection: "out", image: "gmail.jpg" }, + { left: "48%", top: "75%", rotate: "6deg", size: 76, flowDirection: "in", image: "hubspot.jpg" }, + { left: "65%", top: "78%", rotate: "-8deg", size: 72, flowDirection: "out", image: "jira.jpg" }, + { left: "5%", top: "10%", rotate: "10deg", size: 68, flowDirection: "in", image: "obsidian.jpg" }, +] as const; + function Component() { + const containerRef = useRef(null); + const fixedIconRef = useRef(null); + const [fixedIconRect, setFixedIconRect] = useState(null); + const [draggablePositions, setDraggablePositions] = useState<{ x: number; y: number }[]>([]); + + useEffect(() => { + const updateFixedIconRect = () => { + if (fixedIconRef.current) { + setFixedIconRect(fixedIconRef.current.getBoundingClientRect()); + } + }; + + updateFixedIconRect(); + window.addEventListener("resize", updateFixedIconRect); + window.addEventListener("scroll", updateFixedIconRect); + + return () => { + window.removeEventListener("resize", updateFixedIconRect); + window.removeEventListener("scroll", updateFixedIconRect); + }; + }, []); + + const handleDraggablePositionChange = useCallback((index: number, pos: { x: number; y: number }) => { + setDraggablePositions((prev) => { + const newPositions = [...prev]; + newPositions[index] = pos; + return newPositions; + }); + }, []); + return (
-
-
-
- Coming Soon -
-

- Automate your workflow -
- with powerful automation -

-

- Automate repetitive tasks with powerful workflows. No coding required. Coming soon. -

-
- -
-

Workflow automation

-

- Set up workflows once and let them handle repetitive tasks automatically. -

-
-
- -

Auto-process meetings

-

- Automatically transcribe, summarize, and extract action items from all your meetings without manual - intervention. -

-
    -
  • - - Trigger workflows on meeting end -
  • -
  • - - Apply custom templates automatically -
  • -
  • - - Tag and categorize by meeting type -
  • -
-
-
- -

Smart notifications

-

- Get notified when important events happen in your meetings. Set up custom alerts based on keywords, - speakers, or topics. -

-
    -
  • - - Alert on specific keywords mentioned -
  • -
  • - - Notify on action items assigned to you -
  • -
  • - - Daily/weekly digest summaries -
  • -
-
-
-
-
- -

Auto-export

-

- Automatically export notes to specific folders or formats after each meeting. -

-
-
- -

Email summaries

-

- Send meeting summaries to participants or stakeholders automatically. -

-
-
- -

Calendar sync

-

- Attach notes to calendar events and sync action items as tasks. -

-
-
-
- -
-

- Workflow use cases -

-
-
- -

Team standups

-

- Automatically transcribe daily standups, extract updates from each team member, and send a digest to - the team channel. -

-
-
- -

Client meetings

-

- Process client calls, extract action items, create tasks in your project management tool, and send - follow-up emails. -

-
-
- -

Interviews

-

- Transcribe interviews, extract key insights, apply custom evaluation templates, and save to your ATS. -

+ {/* Hero Section */} +
+ {/* SVG for connection lines - fixed position */} + {fixedIconRect && ( + + + + + + + + + + + + + + {DRAGGABLE_ICONS.map((icon, idx) => { + const draggablePos = draggablePositions[idx]; + if (!draggablePos) { + return null; + } + + const startX = fixedIconRect.left + fixedIconRect.width / 2; + const startY = fixedIconRect.top + fixedIconRect.height / 2; + const endX = draggablePos.x + icon.size / 2; + const endY = draggablePos.y + icon.size / 2; + + // Calculate control points for a curved path (quadratic bezier) + const midX = (startX + endX) / 2; + const midY = (startY + endY) / 2; + + // Add some sag/looseness by offsetting the control point perpendicular to the line + const dx = endX - startX; + const dy = endY - startY; + const distance = Math.sqrt(dx * dx + dy * dy); + const sag = distance * 0.15; // 15% sag for looseness + + // Perpendicular offset + const offsetX = -dy / distance * sag; + const offsetY = dx / distance * sag; + + const controlX = midX + offsetX; + const controlY = midY + offsetY; + + const pathData = `M ${startX} ${startY} Q ${controlX} ${controlY} ${endX} ${endY}`; + + return ( + + + + ); + })} + + )} + + {/* Draggable icons */} + {DRAGGABLE_ICONS.map((icon, idx) => ( + handleDraggablePositionChange(idx, pos)} + /> + ))} + +
+
+ {/* Fixed icon - styled like CTA section */} +
+ Hyprnote
-
- -

Sales calls

-

- Record sales calls, identify key objections and questions, log to CRM, and notify relevant team - members. -

+ +

+ Automate your workflow with powerful automation +

+

+ Automate repetitive tasks with powerful workflows. No coding required. +

+
+
-
- -
-

- How workflows work -

-
-
-
- 1 -
-
-

Set triggers

-

- Choose what starts your workflow: meeting end, keyword detection, scheduled time, or manual trigger. -

-
-
-
-
- 2 -
-
-

Add conditions

-

- Add rules to filter when workflows run: meeting duration, participants, tags, or custom conditions. -

-
-
-
-
- 3 -
-
-

Define actions

-

- Choose what happens next: send notifications, export files, create tasks, or connect to - integrations. -

-
-
-
-
- 4 +
+
+ + {/* Integrations Section */} + +
+
+ ); +} + +function IntegrationsSection() { + const integrations = [ + { name: "Slack", image: "slack.jpg", description: "Team communication and collaboration" }, + { name: "Linear", image: "linear.jpg", description: "Issue tracking and project management" }, + { name: "Notion", image: "notion.jpg", description: "All-in-one workspace" }, + { name: "Salesforce", image: "salesforce.jpg", description: "Customer relationship management" }, + { name: "Affinity", image: "affinity.jpg", description: "Relationship intelligence platform" }, + { name: "Attio", image: "attio.jpg", description: "Modern CRM for startups" }, + { name: "Google Calendar", image: "gcal.jpg", description: "Calendar and scheduling" }, + { name: "Gmail", image: "gmail.jpg", description: "Email and communication" }, + { name: "HubSpot", image: "hubspot.jpg", description: "Marketing and sales platform" }, + { name: "Jira", image: "jira.jpg", description: "Project tracking and agile workflows" }, + { name: "Obsidian", image: "obsidian.jpg", description: "Knowledge base and note-taking" }, + ]; + + return ( +
+
+
+

+ Supported Integrations +

+

+ Connect Hyprnote with your favorite tools and platforms +

+
+ +
+ {integrations.map((integration) => ( +
+
+
+ {integration.name}
-

Let it run

-

- Workflows run automatically in the background. Monitor activity and adjust as needed. -

+

{integration.name}

+

{integration.description}

-
- -
-

- Get notified when we launch -

-

- Be the first to know when workflows become available. -

- - Join waitlist - -
+ ))}
+ +
+

+ More integrations coming soon +

+ +
+
+ + ); +} + +function DraggableIcon({ + initialPosition, + size, + image, + onPositionChange, +}: { + initialPosition: { left: string; top: string; rotate: string }; + size: number; + image: string; + onPositionChange?: (pos: { x: number; y: number }) => void; +}) { + const [dragStart, setDragStart] = useState<{ x: number; y: number } | null>(null); + const [position, setPosition] = useState({ x: 0, y: 0 }); + const [scale, setScale] = useState(1); + const iconRef = useRef(null); + + const calculatePosition = useCallback(() => { + if (typeof window === "undefined") { + return { x: 0, y: 0 }; + } + + const container = iconRef.current?.parentElement; + if (!container) { + return { x: 0, y: 0 }; + } + + const containerWidth = container.clientWidth; + const containerHeight = container.clientHeight; + + const x = (containerWidth * parseInt(initialPosition.left)) / 100 - size / 2; + const y = (containerHeight * parseInt(initialPosition.top)) / 100 - size / 2; + + return { x, y }; + }, [initialPosition, size]); + + const getAbsolutePosition = useCallback(() => { + if (!iconRef.current) { + return { x: 0, y: 0 }; + } + const rect = iconRef.current.getBoundingClientRect(); + return { + x: rect.left, + y: rect.top, + }; + }, []); + + useEffect(() => { + const pos = calculatePosition(); + setPosition(pos); + setTimeout(() => { + const absPos = getAbsolutePosition(); + onPositionChange?.(absPos); + }, 0); + }, [calculatePosition, getAbsolutePosition, onPositionChange]); + + useEffect(() => { + const handleResize = () => { + if (!dragStart) { + const pos = calculatePosition(); + setPosition(pos); + setTimeout(() => { + const absPos = getAbsolutePosition(); + onPositionChange?.(absPos); + }, 0); + } + }; + + window.addEventListener("resize", handleResize); + return () => window.removeEventListener("resize", handleResize); + }, [calculatePosition, dragStart, getAbsolutePosition, onPositionChange]); + + const handlePointerDown = (e: React.PointerEvent) => { + if (!iconRef.current) { + return; + } + + iconRef.current.setPointerCapture(e.pointerId); + setScale(1.05); + setDragStart({ + x: e.clientX - position.x, + y: e.clientY - position.y, + }); + }; + + const handlePointerMove = (e: React.PointerEvent) => { + if (!dragStart || !iconRef.current?.hasPointerCapture(e.pointerId)) { + return; + } + + const container = iconRef.current.parentElement; + if (!container) { + return; + } + + const containerRect = container.getBoundingClientRect(); + const newPos = { + x: e.clientX - dragStart.x, + y: e.clientY - dragStart.y, + }; + setPosition(newPos); + + // Calculate absolute position directly instead of waiting for DOM update + const absPos = { + x: containerRect.left + newPos.x, + y: containerRect.top + newPos.y, + }; + onPositionChange?.(absPos); + }; + + const handlePointerUp = (e: React.PointerEvent) => { + if (!iconRef.current) { + return; + } + + iconRef.current.releasePointerCapture(e.pointerId); + setScale(1); + setDragStart(null); + }; + + return ( +
+
+ Integration
); diff --git a/apps/web/src/routes/_view/route.tsx b/apps/web/src/routes/_view/route.tsx index c1fc234635..d5098994b1 100644 --- a/apps/web/src/routes/_view/route.tsx +++ b/apps/web/src/routes/_view/route.tsx @@ -82,7 +82,7 @@ function Header() {
-
+
{link.label} {link.badge && ( @@ -134,7 +134,7 @@ function Header() { {link.label} {link.badge && (