Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 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
11 changes: 9 additions & 2 deletions .cursor/rules/simple.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
alwaysApply: true
---

# Mutation
- Never do manual state management for form/mutation. Things like setError is anti-pattern. use useForm(from tanstack-form) and useQuery/useMutation(from tanstack-query) for 99% cases.

# Comments
- By default, avoid writing comments at all.
- If you write one, it should be about "Why", not "What".
- Do not create summary docs or example code file if not request. Plan is ok.
- After some good amount of typescript change, run `pnpm -r typecheck`.

# Misc
- Do not create summary docs or example code file if not requested. Plan is ok.
- After a significant amount of TypeScript changes, run `pnpm -r typecheck`.
- If there are many classNames and they have conditional logic, use `cn` (import it with `import { cn } from "@hypr/utils"`). It is similar to `clsx`. Always pass an array. Split by logical grouping.
- Use `motion/react` instead of `framer-motion`.

46 changes: 0 additions & 46 deletions apps/desktop/src/hooks/useQuery.ts

This file was deleted.

5 changes: 5 additions & 0 deletions apps/web/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ STRIPE_SECRET_KEY=""
STRIPE_WEBHOOK_SECRET=""

VITE_STRIPE_PUBLISHABLE_KEY=""

VITE_POSTHOG_API_KEY=""
VITE_POSTHOG_HOST="https://us.i.posthog.com"

LOOPS_KEY=""
2 changes: 2 additions & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@nangohq/frontend": "^0.69.7",
"@nangohq/node": "^0.69.7",
"@netlify/vite-plugin-tanstack-start": "^1.1.8",
"@posthog/react": "^1.3.0",
"@sentry/tanstackstart-react": "^10.22.0",
"@stripe/stripe-js": "^8.2.0",
"@supabase/ssr": "^0.7.0",
Expand All @@ -37,6 +38,7 @@
"exa-js": "^1.10.2",
"lucide-react": "^0.544.0",
"postgres": "^3.4.7",
"posthog-js": "^1.284.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"rehype-autolink-headings": "^7.1.0",
Expand Down
Binary file added apps/web/public/hyprnote/calendar.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/hyprnote/chat.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/hyprnote/contacts.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/hyprnote/editor.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/hyprnote/no-bots.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/hyprnote/no-wifi.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/hyprnote/poster-image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/hyprnote/summary.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/web/public/hyprnote/transcript.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/web/public/patterns/dots.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions apps/web/public/patterns/slash.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 3 additions & 15 deletions apps/web/src/components/download-button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { cn } from "@hypr/utils";

import { Icon } from "@iconify-icon/react";
import { Link } from "@tanstack/react-router";

export function DownloadButton() {
Expand All @@ -13,21 +14,8 @@ export function DownloadButton() {
"transition-all",
])}
>
Download now
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth="1.5"
stroke="currentColor"
className="h-5 w-5 ml-2 group-hover:translate-x-1 transition-transform"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="m12.75 15 3-3m0 0-3-3m3 3h-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>
<Icon icon="mdi:apple" className="text-xl mr-2" />
Download for Mac
</Link>
);
}
8 changes: 8 additions & 0 deletions apps/web/src/components/github-open-source.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ export function GitHubOpenSource() {

return (
<section className="border-t border-neutral-100">
<div
className="border-b border-neutral-100 bg-neutral-50 h-4"
style={{ backgroundImage: "url(/patterns/slash.svg)" }}
/>
<div className="px-4 py-8">
<div className="lg:hidden max-w-4xl mx-auto">
<OpenSourceButton showStars={true} starCount={STARS_COUNT} />
Expand Down Expand Up @@ -276,6 +280,10 @@ export function GitHubOpenSource() {
</div>
</div>
</div>
<div
className="border-t border-neutral-100 bg-neutral-50 h-4"
style={{ backgroundImage: "url(/patterns/slash.svg)" }}
/>
</section>
);
}
9 changes: 4 additions & 5 deletions apps/web/src/components/join-waitlist-button.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { cn } from "@hypr/utils";
import { Link } from "@tanstack/react-router";

export function JoinWaitlistButton() {
return (
<a
href="https://tally.so/r/mJaRDY"
target="_blank"
rel="noopener noreferrer"
<Link
to="/join-waitlist"
className={cn([
"group px-6 h-12 flex items-center justify-center text-base sm:text-lg",
"bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full",
Expand All @@ -28,6 +27,6 @@ export function JoinWaitlistButton() {
d="m12.75 15 3-3m0 0-3-3m3 3h-7.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
/>
</svg>
</a>
</Link>
);
}
14 changes: 7 additions & 7 deletions apps/web/src/components/logo-cloud.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function LogoCloud() {
/>

<LogoCard
className="border-b border-neutral-100 md:border-r"
className="md:border-r border-b border-neutral-100"
logo={{
src: "/icons/meta.svg",
alt: "Meta Logo",
Expand All @@ -62,39 +62,39 @@ export function LogoCloud() {
/>

<LogoCard
className="relative border-b border-neutral-100 md:border-r"
className="border-b border-neutral-100 md:border-r"
logo={{
src: "/icons/richmond_american.svg",
alt: "Richmond American Logo",
}}
/>

<LogoCard
className="relative border-b border-neutral-100"
className="relative border-r border-b border-neutral-100 md:border-r-0"
logo={{
src: "/icons/wayfair.svg",
alt: "Wayfair Logo",
}}
/>

<LogoCard
className="md:border-r border-neutral-100"
className="border-b border-neutral-100 md:border-r md:border-b-0"
logo={{
src: "/icons/amazon.svg",
alt: "Amazon Logo",
}}
/>

<LogoCard
className="border-r border-neutral-100"
className="border-r border-b border-neutral-100 md:border-b-0"
logo={{
src: "/icons/palantir.svg",
alt: "Palantir Logo",
}}
/>

<LogoCard
className="border-r border-neutral-100"
className="border-b border-neutral-100 md:border-r md:border-b-0"
logo={{
src: "/icons/disney.svg",
alt: "Disney Logo",
Expand All @@ -110,7 +110,7 @@ export function LogoCloud() {
/>

<LogoCard
className=""
className="border-neutral-100"
logo={{
src: "/icons/bain.svg",
alt: "Bain Logo",
Expand Down
27 changes: 17 additions & 10 deletions apps/web/src/components/social-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ export function SocialCard({
const config = platformConfig[platform];

return (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
<div
className={cn(
"block border border-neutral-100 bg-white p-6 transition-all duration-200 text-left hover:bg-neutral-50",
className,
[
"block border border-neutral-100 bg-white p-6 transition-all duration-200 text-left hover:bg-neutral-50",
className,
],
)}
>
<div className="space-y-4 text-left">
Expand All @@ -70,12 +69,20 @@ export function SocialCard({
</p>
{config.subtitle && <p className="text-sm text-neutral-600">{config.subtitle}</p>}
</div>
<svg className={cn("size-5 shrink-0", config.iconColor)} fill="currentColor" viewBox="0 0 24 24">
<path d={config.iconPath} />
</svg>
<a
href={url}
target="_blank"
rel="noopener noreferrer"
className="hover:opacity-70 transition-opacity"
onClick={(e) => e.stopPropagation()}
>
<svg className={cn("size-5 shrink-0", config.iconColor)} fill="currentColor" viewBox="0 0 24 24">
<path d={config.iconPath} />
</svg>
</a>
</div>
<p className="text-neutral-700 leading-relaxed line-clamp-4 md:line-clamp-15 overflow-hidden">{body}</p>
</div>
</a>
</div>
);
}
6 changes: 1 addition & 5 deletions apps/web/src/components/video-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,16 @@ export function VideoModal({ playbackId, isOpen, onClose }: VideoModalProps) {
className="relative w-full max-w-6xl aspect-video"
onClick={(e) => e.stopPropagation()}
>
{/* Close button */}
<button
onClick={onClose}
className={cn(
"absolute -top-12 right-0 p-2",
"text-white hover:text-neutral-300",
"transition-colors duration-200",
["absolute -top-12 right-0 p-2", "text-white hover:text-neutral-300", "transition-colors duration-200"],
)}
aria-label="Close video"
>
<Icon icon="mdi:close" className="text-3xl" />
</button>

{/* Video */}
<MuxPlayer
ref={playerRef}
playbackId={playbackId}
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/video-thumbnail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ export function VideoThumbnail({
<div className={cn(["relative w-full h-full overflow-hidden group cursor-pointer", className])} onClick={onPlay}>
<MuxPlayer
playbackId={playbackId}
poster="/hyprnote/poster-image.png"
muted
playsInline
className="w-full h-full object-cover pointer-events-none"
className="w-full h-full object-cover pointer-events-none aspect-video"
style={{
"--controls": "none",
aspectRatio: "16/9",
} as React.CSSProperties}
/>

Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const env = createEnv({
VITE_SUPABASE_URL: z.string().min(1),
VITE_SUPABASE_PUBLISHABLE_KEY: z.string().min(1),
VITE_STRIPE_PUBLISHABLE_KEY: z.string().min(1),
VITE_POSTHOG_API_KEY: z.string().optional(),
VITE_POSTHOG_HOST: z.string().default("https://us.i.posthog.com"),
},

runtimeEnv: { ...process.env, ...import.meta.env },
Expand Down
42 changes: 42 additions & 0 deletions apps/web/src/functions/loops.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createServerFn } from "@tanstack/react-start";
import { z } from "zod";

const inputSchema = z.object({
email: z.string().email(),
userGroup: z.string().optional(),
locale: z.string().optional(),
platform: z.string().optional(),
source: z.string().optional(),
intent: z.string().optional(),
});

export const addContact = createServerFn({ method: "POST" })
.inputValidator(inputSchema)
.handler(async ({ data }) => {
const loopsResponse = await fetch(
"https://app.loops.so/api/v1/contacts/create",
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.LOOPS_KEY}`,
},
body: JSON.stringify({
email: data.email,
userGroup: data.userGroup,
locale: data.locale,
source: data.source,
intent: data.intent,
platform: data.platform,
}),
},
);

if (!loopsResponse.ok) {
const error = await loopsResponse.json();
console.error("Error creating contact:", error);
throw new Error(error.message || "Failed to create contact");
}

return { success: true };
});
Loading
Loading