Skip to content

Conversation

@ComputelessComputer
Copy link
Collaborator

No description provided.

@coderabbitai
Copy link

coderabbitai bot commented Oct 26, 2025

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds Mux-based video components and a GitHub open-source showcase, integrates them into the landing route, replaces the old downloads route with a new download page, updates header/branding and small UI tweaks, adds a /cal route, and introduces stylesheet utilities and a Mux player dependency.

Changes

Cohort / File(s) Summary
New Open Source Component
apps/web/src/components/github-open-source.tsx
Adds GitHubOpenSource export with internal helpers (avatars, StatBadge, GridRow) rendering curated contributor avatars and static Stars/Forks badges and CTA. No network calls.
New Video UI Components
apps/web/src/components/video-modal.tsx, apps/web/src/components/video-player.tsx, apps/web/src/components/video-thumbnail.tsx
Adds VideoModal (fullscreen Mux modal with body-scroll lock, Escape/overlay close), VideoPlayer (hover play/pause with overlay actions), and VideoThumbnail (play-overlay thumbnail with onPlay callback).
Landing Page Integration & Layout
apps/web/src/routes/_view/index.tsx
Integrates GitHubOpenSource and video components; adds modal/video state, feature/detail data, scroll-sync helpers, refs, and significant mobile-first layout restructuring.
Route & Header Updates
apps/web/src/routes/_view/route.tsx
Replaces text brand with image logo, adjusts nav links/spacing, removes HeaderUser, replaces Downloads link with external waitlist, and updates header/footer padding.
Downloads / Download Page Changes
apps/web/src/routes/_view/downloads.tsx (deleted), apps/web/src/routes/_view/download.tsx (added)
Removes old downloads route file and adds a new / _view/download route rendering a Download landing page with grouped DownloadCard components and per-platform states.
Auth & Account Tweaks
apps/web/src/routes/auth.tsx, apps/web/src/routes/_view/app/account.tsx
Simplifies auth layout, updates header image path, adjusts padding and border-color in account-card styling; minor cleanup.
New Calendar Route
apps/web/src/routes/cal.tsx
Adds client-side /cal route with a booking CTA page and FAQ.
UI Components & Small Tweaks
apps/web/src/components/join-waitlist-button.tsx, apps/web/src/components/logo-cloud.tsx, apps/web/src/components/social-card.tsx, apps/web/src/components/github-stars.tsx, apps/web/src/components/download-button.tsx, apps/web/src/components/not-found.tsx
Adds JoinWaitlistButton; removes cursor-pointer and adjusts logo sizes; tightens small-screen line-clamp in SocialCard; minor whitespace change in GithubStars; changes download link targets from /downloads to /download.
Removed Testimonial Component
apps/web/src/components/testimonial-card.tsx
Deletes exported TestimonialCard and its props type.
Styles & Utilities
apps/web/src/styles.css
Adds --breakpoint-laptop CSS variable and .scrollbar-none utility to hide scrollbars across browsers.
Package Dependency
apps/web/package.json
Adds dependency @mux/mux-player-react ^3.7.0.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as User (Browser)
  participant Landing as LandingPage (index.tsx)
  participant Thumbnail as VideoThumbnail
  participant Player as VideoPlayer
  participant Modal as VideoModal
  participant OSS as GitHubOpenSource

  Browser->>Landing: load page
  Landing->>Thumbnail: render thumbnail(s)
  Browser->>Thumbnail: hover / click
  alt hover (desktop)
    Thumbnail->>Player: attempt hover-play & show controls
  end
  Browser->>Player: click expand
  Player->>Landing: request open modal (set expandedVideo)
  Landing->>Modal: render isOpen=true with playbackId
  Modal->>Player: invoke play on player ref (autoplay)
  Browser->>OSS: view avatars / click CTA
  OSS-->>Browser: navigate to GitHub repo
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing focused review:
    • apps/web/src/routes/_view/index.tsx — layout rework, state/ref wiring, scroll-sync and carousel logic.
    • apps/web/src/components/video-modal.tsx & video-player.tsx — accessibility, body-scroll lock, autoplay/ref usage, hover play/pause semantics.
    • apps/web/src/components/github-open-source.tsx — grid slicing, contributor data, and number-formatting logic.
    • apps/web/package.json — ensure new Mux dependency aligns with build tooling.

Possibly related PRs

  • landing wip #1597 — overlaps changes to downloads route files (adds/removes related routes).
  • landing-3 #1599 — touches not-found and navigation link changes referenced here.
  • landing-2 #1598 — related edits around download/landing UI and GitHub stars components.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 2 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "landing-4" is related to the main focus of the changeset, as the pull request primarily consists of landing page updates, new components, and route restructuring. However, the title uses a non-descriptive, generic term that does not convey meaningful information about what specifically has changed. A reader scanning the pull request history would not understand from this title alone whether it involves feature additions, bug fixes, refactoring, styling changes, or component restructuring, making it vague despite being conceptually related to the landing page updates.
Description Check ❓ Inconclusive The pull request has no description provided by the author. While the changeset is substantial and includes multiple new components (video player, modal, thumbnail), new pages (pricing, download, calendar), and significant structural updates to the landing page, there is no accompanying description to contextualize these changes or explain the intent. An empty description conveys no meaningful information and cannot be evaluated as related to the changeset, falling into the category of being too vague to assess.

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cb767a6 and e0df931.

📒 Files selected for processing (1)
  • apps/web/src/routes/_view/pricing.tsx (1 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (8)
apps/web/src/routes/_view/downloads.tsx (1)

61-61: LGTM. Add a focus-visible ring for keyboard users.

Improves a11y without changing visuals on mouse.

- className="group flex items-center justify-between p-6 rounded-xl border border-neutral-100 hover:border-blue-300 hover:bg-blue-50/30 transition-all duration-200"
+ className="group flex items-center justify-between p-6 rounded-xl border border-neutral-100 hover:border-blue-300 hover:bg-blue-50/30 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-1"
apps/web/src/routes/_view/route.tsx (2)

30-31: Prevent layout shift on logo.

Specify intrinsic size and decoding hints.

- <img src="/hyprnote-logo.svg" alt="Hyprnote" className="h-6" />
+ <img
+   src="/hyprnote-logo.svg"
+   alt="Hyprnote"
+   className="h-6 w-auto"
+   width="96"
+   height="24"
+   decoding="async"
+   fetchpriority="high"
+ />

34-47: Avoid blanket transition-all on text links.

Use transition-colors and add underline offset for clearer affordance. Lighter on the browser.

- className="text-sm text-neutral-600 hover:text-neutral-800 transition-all hover:underline decoration-dotted"
+ className="text-sm text-neutral-600 hover:text-neutral-800 transition-colors hover:underline decoration-dotted underline-offset-4"

Apply to Docs, Blog, Pricing, Account, and Get Started.

Also applies to: 75-87

apps/web/src/routes/auth.tsx (1)

103-115: Add a visible or sr-only label for the email field.

Placeholders aren’t labels; improves screen-reader UX.

+ <label htmlFor="email" className="sr-only">Email</label>
  <input
    id="email"
    type="email"
    ...
  />
apps/web/src/components/github-open-source.tsx (3)

30-37: Avatar a11y/perf and border consistency.

  • Avatars are decorative—use empty alt and lazy loading.
  • Align borders with the rest of the app (neutral-100).
- className="size-10 rounded-sm overflow-hidden border-2 border-neutral-200 bg-neutral-100"
+ className="size-10 rounded-sm overflow-hidden border border-neutral-100 bg-neutral-100"
...
- <img
-   src={avatar}
-   alt="Contributor"
-   className="w-full h-full object-cover"
- />
+ <img
+   src={avatar}
+   alt=""
+   loading="lazy"
+   decoding="async"
+   className="w-full h-full object-cover"
+ />

And in StatBadge:

- <div className="flex flex-col gap-2 h-24 items-center justify-center border border-neutral-200 rounded-sm px-4 bg-neutral-100">
+ <div className="flex flex-col gap-2 h-24 items-center justify-center border border-neutral-100 rounded-sm px-4 bg-neutral-100">

Also applies to: 53-60


64-76: Hard-coded GitHub counts will go stale.

Consider a small query (like GithubStars) or pass counts as props with a fallback.

Example (sketch):

export function GitHubOpenSource({ stars, forks }: { stars?: number; forks?: number } = {}) {
  const STARS_COUNT = stars ?? 6419;
  const FORKS_COUNT = forks ?? 396;
  ...
}

Or reuse a lightweight useQuery to fetch stargazers_count and forks.


4-4: DRY for ORG_REPO constant.

It appears in multiple files; extract to a shared constant (e.g., @/constants/github.ts).

apps/web/src/routes/_view/index.tsx (1)

460-462: Stray border color with no border width.

border-neutral-100 alone has no effect. Either remove it or add a border side.

- <div className="aspect-video md:aspect-auto bg-neutral-50 flex items-center justify-center border-neutral-100">
+ <div className="aspect-video md:aspect-auto bg-neutral-50 flex items-center justify-center">
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7fca2a2 and ae25248.

⛔ Files ignored due to path filters (5)
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/hyprnote-logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote_signature_light.svg is excluded by !**/*.svg
  • apps/web/public/team/john.png is excluded by !**/*.png
  • apps/web/public/team/yujong.png is excluded by !**/*.png
📒 Files selected for processing (6)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/downloads.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (6 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
  • apps/web/src/routes/auth.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/routes/auth.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/routes/_view/downloads.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/routes/auth.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/routes/_view/downloads.tsx
🧬 Code graph analysis (2)
apps/web/src/routes/_view/index.tsx (5)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (64-110)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
apps/web/src/components/github-open-source.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 39-39: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (3)
apps/web/src/routes/auth.tsx (1)

89-90: Border color consistency looks good.

Matches the neutral-100 pass across the app. No functional impact.

Also applies to: 92-97, 155-156

apps/web/src/routes/_view/app/account.tsx (1)

171-186: LGTM on border updates.

Consistent with the new neutral-100 theme.

Also applies to: 193-213

apps/web/src/routes/_view/index.tsx (1)

1-18: Run typecheck in your local development environment to verify no type drift from TSX movements.

The sandbox environment lacks the deno interpreter needed to run the full typecheck script (deno check supabase/functions/**/*.ts && tsc --noEmit). Please execute the following in your local development environment to ensure the substantial TSX changes don't introduce type errors:

pnpm -r typecheck
pnpm -r lint

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/web/src/routes/_view/index.tsx (1)

36-43: Security: Add rel="noopener noreferrer" to external link.

This link opens in a new tab without security attributes, enabling potential tab-nabbing attacks.

Apply this diff:

 <a
   className="decoration-dotted underline hover:text-stone-600 transition-all"
   href="https://github.com/fastrepl/hyprnote"
   target="_blank"
+  rel="noopener noreferrer"
 >
apps/web/src/components/github-open-source.tsx (1)

23-41: Fix dynamic Tailwind class that will be purged.

Template-interpolated class names are not detectable by Tailwind's purge mechanism and will break in production.

Apply this diff to use explicit class variants:

+import { cn } from "@hypr/utils";
+
 function ProfileGrid({ profiles, cols }: { profiles: string[]; cols: 2 | 3 }) {
   const count = cols === 2 ? 4 : 6;
   return (
-    <div className={`grid grid-cols-${cols} gap-3`}>
+    <div className={cn([
+      "grid gap-3",
+      cols === 2 ? "grid-cols-2" : "grid-cols-3",
+    ])}>
       {profiles.slice(0, count).map((avatar, idx) => (
🧹 Nitpick comments (2)
apps/web/src/components/github-open-source.tsx (2)

7-21: Address TODO comment for curated profiles.

The TODO comment indicates this list may need completion or curation.

Would you like me to help generate a more comprehensive contributor list or create an issue to track this task?


64-110: Consider fetching GitHub stats dynamically.

The component uses hardcoded counts while the GithubStars component (lines 4-34 in github-stars.tsx) fetches live data from the GitHub API. Consider unifying the approach for consistency and accuracy.

Example refactor to fetch counts dynamically:

import { useQuery } from "@tanstack/react-query";

export function GitHubOpenSource() {
  const stats = useQuery({
    queryKey: ["github-repo-stats"],
    queryFn: async () => {
      const response = await fetch(`https://api.github.com/repos/${ORG_REPO}`);
      const data = await response.json();
      return {
        stars: data.stargazers_count ?? 6419,
        forks: data.forks_count ?? 396,
      };
    },
  });

  const STARS_COUNT = stats.data?.stars ?? 6419;
  const FORKS_COUNT = stats.data?.forks ?? 396;
  
  // ... rest of component
}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae25248 and 822609e.

⛔ Files ignored due to path filters (7)
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/hyprnote-logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote_signature_light.svg is excluded by !**/*.svg
  • apps/web/public/patterns/paper.png is excluded by !**/*.png
  • apps/web/public/patterns/white_leather.png is excluded by !**/*.png
  • apps/web/public/team/john.png is excluded by !**/*.png
  • apps/web/public/team/yujong.png is excluded by !**/*.png
📒 Files selected for processing (6)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/downloads.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (6 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
  • apps/web/src/routes/auth.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/routes/auth.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/routes/_view/downloads.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/routes/_view/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/routes/_view/index.tsx
🧬 Code graph analysis (2)
apps/web/src/components/github-open-source.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/index.tsx (5)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (64-110)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 39-39: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🪛 GitHub Actions: .github/workflows/fmt.yaml
apps/web/src/routes/_view/index.tsx

[error] 470-496: dprint formatting check failed. Found 1 not formatted file.

🔇 Additional comments (4)
apps/web/src/routes/_view/app/account.tsx (1)

171-171: LGTM! Consistent border color refresh.

The border color updates from border-neutral-200 to border-neutral-100 are applied consistently across both the AccountSettingsCard and IntegrationsSettingsCard components, creating a more subtle visual design. This aligns with the broader UI-wide border color refresh mentioned in the summary.

Also applies to: 179-179, 193-193, 201-201

apps/web/src/routes/_view/index.tsx (1)

14-541: Well-structured landing page layout.

The restructured layout effectively integrates the new GitHubOpenSource component alongside reorganized hero, features, social proof, and manifesto sections. Responsive design patterns are properly implemented with mobile-first and desktop variants.

Per coding guidelines, run typecheck after these substantial TypeScript changes:

#!/bin/bash
pnpm -r typecheck
apps/web/src/components/github-open-source.tsx (2)

43-62: LGTM!

The StatBadge component correctly formats counts and handles both stars and forks display types.


85-97: Excellent security and styling practices.

The CTA link properly includes rel="noopener noreferrer" for security, and the cn helper is used correctly with array format and logical grouping per coding guidelines.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/web/src/routes/_view/index.tsx (1)

36-43: Security: target="_blank" missing rel attributes

Add rel="noopener noreferrer" to prevent tab‑nabbing. This was flagged earlier; applying here resolves the lint warning.

- <a
+ <a
     className="decoration-dotted underline hover:text-stone-600 transition-all"
     href="https://github.com/fastrepl/hyprnote"
     target="_blank"
+    rel="noopener noreferrer"
   >
     open source
   </a>
🧹 Nitpick comments (5)
apps/web/src/routes/_view/route.tsx (2)

34-35: Prefer targeted transitions over transition-all for perf

Avoid transition-all on simple text color hovers; use transition-colors. Keeps paint scope tight and consistent with the rest of the app.

- className="text-sm text-neutral-600 hover:text-neutral-800 transition-all hover:underline decoration-dotted"
+ className="text-sm text-neutral-600 hover:text-neutral-800 transition-colors hover:underline decoration-dotted"

Apply the same replacement to the Blog, Pricing, Account, and Get Started links in this file.

Also applies to: 40-41, 46-47, 75-76, 87-88


28-31: Optional: add intrinsic size to logo img to reduce CLS

Adding width/height improves layout stability without affecting styling.

- <img src="/hyprnote-logo.svg" alt="Hyprnote" className="h-6" />
+ <img src="/hyprnote-logo.svg" alt="Hyprnote" className="h-6" width="24" height="24" />
apps/web/src/routes/auth.tsx (1)

196-232: Harden OAuth button: type, error path, and URL allow‑list

  • Explicit type avoids accidental form submits if moved.
  • Basic onError avoids silent failures.
  • Allow‑list redirect origins to reduce open‑redirect risks.
-function OAuthButton({ flow, provider }: { flow: "desktop" | "web"; provider: "google" | "github" }) {
+function OAuthButton({ flow, provider }: { flow: "desktop" | "web"; provider: "google" | "github" }) {
   const oauthMutation = useMutation({
     mutationFn: (provider: "google" | "github") =>
       doAuth({
         data: {
           method: "oauth",
           provider,
           flow,
         },
       }),
-    onSuccess: (result) => {
-      if (result?.url) {
-        window.location.href = result.url;
-      }
-    },
+    onSuccess: (result) => {
+      if (result?.url) {
+        try {
+          const allowed = new Set(["https://accounts.google.com", "https://github.com"]);
+          const u = new URL(result.url);
+          if (allowed.has(u.origin)) window.location.assign(result.url);
+        } catch {
+          /* no-op */
+        }
+      }
+    },
+    onError: () => {
+      // TODO: surface a toast or inline error
+    },
   });
   return (
-    <button
+    <button
+      type="button"
       onClick={() => oauthMutation.mutate(provider)}
       disabled={oauthMutation.isPending}
apps/web/src/routes/_view/index.tsx (2)

287-289: Copy tweaks

Two small phrasing fixes for polish.

- <h2 className="text-3xl font-serif text-stone-600 mb-4">Hyprnote works like charm</h2>
+ <h2 className="text-3xl font-serif text-stone-600 mb-4">Hyprnote works like a charm</h2>
- <h2 className="text-3xl font-serif text-stone-600 mb-4">We focus on every bit of details</h2>
+ <h2 className="text-3xl font-serif text-stone-600 mb-4">We focus on every detail</h2>

Also applies to: 384-386


281-286: Defer non-critical images and set decoding for paint speed

Use loading="lazy" and decoding="async" on offscreen/decorative images.

- <img
+ <img
   src="/hyprnote_with_noise.png"
   alt="Hyprnote"
-  className="size-24 rounded-3xl border border-neutral-100"
+  className="size-24 rounded-3xl border border-neutral-100"
+  loading="lazy"
+  decoding="async"
 />
- <img
+ <img
   src="/team/john.png"
   alt="John Jeong"
   className="size-8 rounded-full object-cover border border-neutral-200"
+  loading="lazy"
+  decoding="async"
 />
- <img
+ <img
   src="/hyprnote_signature_light.svg"
   alt="Hyprnote Signature"
   className="w-32 h-auto opacity-80"
+  loading="lazy"
+  decoding="async"
 />

Also applies to: 486-496, 507-511

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 822609e and 75b9999.

⛔ Files ignored due to path filters (7)
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/hyprnote-logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote_signature_light.svg is excluded by !**/*.svg
  • apps/web/public/patterns/paper.png is excluded by !**/*.png
  • apps/web/public/patterns/white_leather.png is excluded by !**/*.png
  • apps/web/public/team/john.png is excluded by !**/*.png
  • apps/web/public/team/yujong.png is excluded by !**/*.png
📒 Files selected for processing (6)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/downloads.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (6 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
  • apps/web/src/routes/auth.tsx (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/routes/_view/app/account.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/routes/_view/downloads.tsx
  • apps/web/src/components/github-open-source.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/routes/auth.tsx
  • apps/web/src/routes/_view/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/routes/auth.tsx
  • apps/web/src/routes/_view/index.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/index.tsx (5)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (64-111)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 39-39: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🪛 GitHub Actions: .github/workflows/fmt.yaml
apps/web/src/routes/auth.tsx

[error] 136-140: dprint formatting check failed. 2 files not formatted. Command: ~/.dprint/bin/dprint check --config 'dprint.json'. Please run 'dprint --config dprint.json --write' to fix code style issues in this file.

apps/web/src/routes/_view/index.tsx

[error] 470-497: dprint formatting check failed. 2 files not formatted. Command: ~/.dprint/bin/dprint check --config 'dprint.json'. Please run 'dprint --config dprint.json --write' to fix code style issues in this file.

🔇 Additional comments (2)
apps/web/src/routes/auth.tsx (1)

138-193: Remove this review comment—the suggested fix command does not exist in the repository

The suggested command pnpm -r fmt fails because there are no fmt or format npm scripts in the codebase. The repo uses dprint directly via CI workflows (.github/workflows/fmt.yaml), not npm scripts. Additionally, the code in the snippet is properly formatted according to TypeScript standards and uses cn() correctly with array formatting and logical grouping.

Likely an incorrect or invalid review comment.

apps/web/src/routes/_view/index.tsx (1)

470-497: Add rel="noopener noreferrer" to the unsafe target="_blank" link at line 39 and review pre-existing unsafe anchors across the repo

The code at lines 470-497 is properly formatted and introduces no new rel issues. However, the existing target="_blank" link at line 39 (GitHub link in the CTA section) lacks the required rel attribute for security. While this is a pre-existing issue (unchanged by this PR), it should be fixed. Additionally, 7 other pre-existing unsafe target="_blank" anchors were found in the repository:

  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/participants.tsx:95
  • apps/desktop/src/components/main/body/sessions/outer-header/metadata/others.tsx:91
  • apps/web/src/components/social-card.tsx:58
  • apps/web/src/components/github-open-source.tsx:87
  • apps/web/src/components/github-stars.tsx:21
  • README.md:7,8

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
apps/web/src/routes/_view/index.tsx (2)

36-43: Security: target="_blank" missing rel attributes.

This security issue was previously flagged and remains unaddressed. Add rel="noopener noreferrer" to prevent tab-nabbing.

Apply this diff:

  <a
    className="decoration-dotted underline hover:text-stone-600 transition-all"
    href="https://github.com/fastrepl/hyprnote"
    target="_blank"
+   rel="noopener noreferrer"
  >

459-462: Visual bug: border color without border width.

Similar to the previous comment on this section, border-neutral-100 has no effect without specifying a border width.

Apply this diff:

- <div className="aspect-video md:aspect-auto bg-neutral-50 flex items-center justify-center border-neutral-100">
+ <div className="aspect-video md:aspect-auto bg-neutral-50 flex items-center justify-center border border-neutral-100">
🧹 Nitpick comments (1)
apps/web/src/routes/_view/index.tsx (1)

1-557: Consider running typecheck after substantial changes.

Per the coding guidelines, after substantial TypeScript changes, consider running the typecheck command to ensure type safety across the codebase.

Based on coding guidelines.

#!/bin/bash
# Run typecheck across all packages
pnpm -r typecheck
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75b9999 and 666f014.

📒 Files selected for processing (2)
  • apps/web/src/routes/_view/index.tsx (6 hunks)
  • apps/web/src/routes/auth.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • apps/web/src/routes/auth.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/index.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/index.tsx (5)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (64-111)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 39-39: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (7)
apps/web/src/routes/_view/index.tsx (7)

5-5: LGTM: Import added correctly.

The GitHubOpenSource component import is properly structured and matches the usage at line 468.


47-89: LGTM: Responsive video and feature cards layout.

The mobile-first approach with proper responsive breakpoints is well-implemented. The placeholder content is appropriate for pre-launch state.


93-275: LGTM: Social proof section well-structured.

The responsive testimonials layout with horizontal scroll on mobile and grid on desktop is well-implemented. The SocialCard component properly handles external links with security attributes.


277-379: LGTM: Features section grid layout.

The feature cards grid with responsive column spans is well-organized. The "Coming Soon" badge appropriately indicates the Daily Note feature status.


467-468: LGTM: GitHubOpenSource component integrated.

The component is properly imported and integrated into the page structure with appropriate section boundaries.


470-531: LGTM: Manifesto section with decorative design.

The postcard-style manifesto section with background patterns and team signatures is well-crafted. The content effectively communicates the product philosophy.


533-553: LGTM: Final CTA section.

The closing section with dual CTAs (DownloadButton and GithubStars) provides clear next steps for users. The styling is consistent with the page's design system.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/routes/_view/route.tsx (1)

56-59: Fix Tailwind gradient class name: change bg-linear-to-t to bg-gradient-to-t.

The review comment is accurate. bg-linear-to-t is not a standard Tailwind class; the correct syntax is bg-gradient-to-t. No custom Tailwind configuration or CSS definitions for bg-linear-to-* were found in the codebase, confirming this is a typo that needs correction.

- className="px-4 h-8 flex items-center text-sm bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
+ className="px-4 h-8 flex items-center text-sm bg-gradient-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
♻️ Duplicate comments (3)
apps/web/src/routes/_view/index.tsx (3)

70-76: Security: add rel to external link opening in a new tab.

target="_blank" without rel enables tab‑nabbing. Add rel="noopener noreferrer".

-                  <a
+                  <a
                     className="decoration-dotted underline hover:text-stone-600 transition-all"
                     href="https://github.com/fastrepl/hyprnote"
                     target="_blank"
+                    rel="noopener noreferrer"
                   >

511-517: Border color without border width.

border-neutral-100 won’t render without a border width.

-              <div className="aspect-video md:aspect-auto border-neutral-100 overflow-hidden">
+              <div className="aspect-video md:aspect-auto border border-neutral-100 overflow-hidden">

1-14: Add missing rel attribute to external link.

In the GitHub link (line 73), add rel="noopener noreferrer" when using target="_blank" for security:

<a
  className="decoration-dotted underline hover:text-stone-600 transition-all"
  href="https://github.com/fastrepl/hyprnote"
  target="_blank"
  rel="noopener noreferrer"
>
  open source
</a>

TypeCheck verification note: The sandbox environment lacks deno, preventing pnpm -r typecheck execution. This should be verified locally before merging, per coding guidelines. The cn() utility usage in the file correctly follows array-based class grouping conventions.

🧹 Nitpick comments (14)
apps/web/src/routes/_view/route.tsx (2)

28-31: Brand link wraps an image; remove text/font classes and add intrinsic size.

Font-related classes on the Link are redundant when the child is an . Also add width/height to reduce CLS.

- className="font-semibold text-2xl font-serif hover:scale-105 transition-transform mr-4"
+ className="hover:scale-105 transition-transform mr-4"
...
- <img src="/hyprnote/logo.svg" alt="Hyprnote" className="h-6" />
+ <img src="/hyprnote/logo.svg" alt="Hyprnote" width="96" height="24" className="h-6 w-auto" />

22-64: Consider reducing transition-all usage.

transition-colors/transition-transform are cheaper and avoid layout/repaint side effects.

apps/web/src/components/video-modal.tsx (3)

50-58: Add dialog semantics and basic focus management.

Add role="dialog", aria-modal, and focus the close button on open; this improves accessibility. A full focus trap (e.g., with @radix-ui/react-dialog) would be ideal.

-    <div
-      className="fixed inset-0 z-50 flex items-center justify-center bg-black/90 p-4"
-      onClick={onClose}
-    >
+    <div
+      className="fixed inset-0 z-50 flex items-center justify-center bg-black/90 p-4"
+      role="dialog"
+      aria-modal="true"
+      aria-labelledby="video-modal-title"
+      onClick={onClose}
+    >
...
-      <div
+      <div
         ref={modalRef}
         className="relative w-full max-w-6xl aspect-video"
         onClick={(e) => e.stopPropagation()}
       >
+        <h2 id="video-modal-title" className="sr-only">Hyprnote Feature Demo</h2>

And focus the close button:

-  const modalRef = useRef<HTMLDivElement>(null);
+  const modalRef = useRef<HTMLDivElement>(null);
+  const closeBtnRef = useRef<HTMLButtonElement>(null);
...
-        <button
+        <button
+          ref={closeBtnRef}

Add effect:

+  useEffect(() => {
+    if (isOpen) closeBtnRef.current?.focus();
+  }, [isOpen]);

62-67: Follow cn usage guideline: pass an array of grouped segments.

Per guidelines, use cn with an array and group logically.

-          className={cn(
-            "absolute -top-12 right-0 p-2",
-            "text-white hover:text-neutral-300",
-            "transition-colors duration-200",
-          )}
+          className={cn([
+            "absolute -top-12 right-0 p-2",
+            "text-white hover:text-neutral-300",
+            "transition-colors duration-200",
+          ])}

61-71: Close button positioning risk on small viewports.

-top-12 may render the button off-canvas for small videos. Consider placing it inside the frame (top-2 right-2) with a subtle background.

- className={cn(["absolute -top-12 right-0 p-2", ...])}
+ className={cn(["absolute top-2 right-2 p-2 rounded bg-black/30 hover:bg-black/40", "text-white", "transition-colors"])}
apps/web/src/components/video-thumbnail.tsx (3)

17-28: Use a static thumbnail image instead of mounting a MuxPlayer for an image.

MuxPlayer is heavy for a poster-only thumbnail. Use Mux’s image endpoint for better perf and LCP.

-      <MuxPlayer
-        playbackId={playbackId}
-        muted
-        playsInline
-        className="w-full h-full object-cover pointer-events-none"
-        style={{
-          "--controls": "none",
-          aspectRatio: "16/9",
-        } as React.CSSProperties}
-      />
+      <img
+        src={`https://image.mux.com/${playbackId}/thumbnail.jpg?time=0`}
+        alt=""
+        loading="lazy"
+        width="1280"
+        height="720"
+        className="w-full h-full object-cover pointer-events-none"
+        style={{ aspectRatio: "16/9" } as React.CSSProperties}
+      />

17-17: Make the container keyboard-accessible.

Clickable divs should be focusable and respond to Enter/Space.

-    <div className={cn("relative w-full h-full overflow-hidden group cursor-pointer", className)} onClick={onPlay}>
+    <div
+      role="button"
+      tabIndex={0}
+      onKeyDown={(e) => (e.key === "Enter" || e.key === " ") && onPlay?.()}
+      className={cn(["relative w-full h-full overflow-hidden group cursor-pointer", className])}
+      onClick={onPlay}
+    >

37-43: Follow cn array style guideline and add focus-visible state.

Use cn([...]) and provide a visible keyboard focus on the play button.

-          className={cn(
-            "size-16 rounded-full bg-white/90 backdrop-blur-sm",
-            "flex items-center justify-center",
-            "hover:bg-white hover:scale-110 transition-all duration-200",
-            "shadow-xl",
-          )}
+          className={cn([
+            "size-16 rounded-full bg-white/90 backdrop-blur-sm",
+            "flex items-center justify-center shadow-xl",
+            "hover:bg-white hover:scale-110 transition-all duration-200",
+            "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/80 focus-visible:ring-offset-2 focus-visible:ring-offset-black/20",
+          ])}
apps/web/src/components/video-player.tsx (3)

24-37: Guard against unnecessary play/pause churn and respect reduced motion.

Avoid toggling on every minor hover flicker; also skip autoplay for users preferring reduced motion.

-  useEffect(() => {
-    if (playerRef.current) {
-      if (isHovered) {
-        playerRef.current.play().catch(() => {
-          // Ignore autoplay errors
-        });
-        setShowControls(true);
-      } else {
-        playerRef.current.pause();
-        playerRef.current.currentTime = 0;
-        setShowControls(false);
-      }
-    }
-  }, [isHovered]);
+  useEffect(() => {
+    const el = playerRef.current;
+    if (!el) return;
+    const prefersReduced = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches;
+    if (isHovered && !prefersReduced) {
+      el.play().catch(() => {});
+      setShowControls(true);
+    } else {
+      el.pause();
+      try { el.currentTime = 0; } catch {}
+      setShowControls(false);
+    }
+  }, [isHovered]);

41-57: Follow cn array style; consider preload="metadata".

Align with cn guideline and reduce network cost when idle.

-      className={cn("relative w-full h-full overflow-hidden group", className)}
+      className={cn(["relative w-full h-full overflow-hidden group", className])}
...
-      <MuxPlayer
+      <MuxPlayer
         ref={playerRef}
         playbackId={playbackId}
         loop
         muted
         playsInline
+        preload="metadata"
         accentColor="#78716c"
         className="w-full h-full object-cover"
         style={{
           "--controls": "none",
           aspectRatio: "16/9",
         } as React.CSSProperties}
       />

60-100: Keyboard parity for overlay and cn array usage.

Show overlay when focused, not just hovered; convert cn calls to array form.

-    <div
+    <div
       ...
-      onMouseEnter={() => setIsHovered(true)}
-      onMouseLeave={() => setIsHovered(false)}
+      onMouseEnter={() => setIsHovered(true)}
+      onMouseLeave={() => setIsHovered(false)}
+      onFocus={() => setIsHovered(true)}
+      onBlur={() => setIsHovered(false)}
     >
...
-        <div
-          className={cn(
+        <div
+          className={cn([
             "absolute bottom-0 left-0 right-0",
             "transition-all duration-300 ease-out",
             "flex gap-0",
-            isHovered ? "translate-y-0 opacity-100" : "translate-y-full opacity-0",
-          )}
+            isHovered ? "translate-y-0 opacity-100" : "translate-y-full opacity-0",
+          ])}
         >
...
-            className={cn(
+            className={cn([
               "flex-1 py-4 text-xs font-mono",
               "bg-stone-100/95 text-stone-800",
               "border-r border-stone-400/50",
               "hover:bg-stone-200/95 active:bg-stone-400/95",
               "transition-all duration-150",
               "backdrop-blur-sm",
-            )}
+            ])}
           >
...
-            className={cn(
+            className={cn([
               "flex-1 py-4 text-xs font-mono",
               "bg-stone-100/95 text-stone-800",
               "hover:bg-stone-200/95 active:bg-stone-400/95",
               "transition-all duration-150",
               "backdrop-blur-sm",
-            )}
+            ])}
apps/web/src/routes/_view/index.tsx (3)

34-41: Use cn with an array of class segments.

Align with project guideline to pass arrays to cn for readability.

As per coding guidelines.

-          <div
-            className={cn(
-              "flex items-center justify-center gap-2 text-center",
-              "bg-stone-50/70 border-b border-stone-100",
-              "py-3 px-4",
-              "font-serif text-sm text-stone-700",
-              "hover:bg-stone-50 transition-all",
-            )}
-          >
+          <div
+            className={cn([
+              "flex items-center justify-center gap-2 text-center",
+              "bg-stone-50/70 border-b border-stone-100",
+              "py-3 px-4",
+              "font-serif text-sm text-stone-700",
+              "hover:bg-stone-50 transition-all",
+            ])}
+          >
-              className={cn("h-4 w-4 inline-block group-hover:scale-105")}
+              className={cn(["h-4 w-4 inline-block group-hover:scale-105"])}

Also applies to: 46-46


331-333: Prefer router navigation over window.location.

Avoid full reloads; use Link or useNavigate from @tanstack/react-router for client routing.

As per coding guidelines.

Also applies to: 349-353, 371-374, 391-394, 513-516


410-410: Lazy‑load non‑critical images.

Improve performance by adding loading="lazy" to below‑the‑fold images.

-                <img src="/static.gif" alt="Daily Note feature" className="w-full h-full object-cover" />
+                <img src="/static.gif" alt="Daily Note feature" className="w-full h-full object-cover" loading="lazy" />
-                  <img
+                  <img
                     src="/team/john.png"
                     alt="John Jeong"
-                    className="size-8 rounded-full object-cover border border-neutral-200"
+                    className="size-8 rounded-full object-cover border border-neutral-200"
+                    loading="lazy"
                   />
-                  <img
+                  <img
                     src="/team/yujong.png"
                     alt="Yujong Lee"
-                    className="size-8 rounded-full object-cover border border-neutral-200"
+                    className="size-8 rounded-full object-cover border border-neutral-200"
+                    loading="lazy"
                   />
-                    <img
+                    <img
                       src="/hyprnote/signature.svg"
                       alt="Hyprnote Signature"
                       className="w-32 h-auto opacity-80"
+                      loading="lazy"
                     />

Also applies to: 556-559, 561-564, 577-580

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 666f014 and 5659139.

⛔ Files ignored due to path filters (6)
  • apps/web/public/hyprnote/icon.png is excluded by !**/*.png
  • apps/web/public/hyprnote/logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote/signature.svg is excluded by !**/*.svg
  • apps/web/public/icons/yc_stone.svg is excluded by !**/*.svg
  • apps/web/public/static.gif is excluded by !**/*.gif
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • apps/web/package.json (1 hunks)
  • apps/web/src/components/video-modal.tsx (1 hunks)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
  • apps/web/src/routes/auth.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/routes/auth.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/video-modal.tsx
  • apps/web/src/components/video-thumbnail.tsx
  • apps/web/src/components/video-player.tsx
  • apps/web/src/routes/_view/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/video-modal.tsx
  • apps/web/src/components/video-thumbnail.tsx
  • apps/web/src/components/video-player.tsx
  • apps/web/src/routes/_view/index.tsx
🧬 Code graph analysis (4)
apps/web/src/components/video-modal.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/video-thumbnail.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/video-player.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/index.tsx (9)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (11-50)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (13-104)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (64-111)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (12-88)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 73-73: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

Comment on lines +22 to +24
const playerRef = useRef<MuxPlayerRefAttributes>(null);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Type of playerRef — confirm compatibility with current @mux/mux-player-react types.

Ensure MuxPlayerRefAttributes matches the ref element’s shape under React 19; update if package types changed.


🏁 Script executed:

#!/bin/bash
pnpm -r typecheck
rg -n "MuxPlayerRefAttributes|HTMLMuxPlayerElement" -S

Length of output: 841



---

🏁 Script executed:

```shell
#!/bin/bash
# Find package.json files and check @mux/mux-player-react version
fd -t f "package.json" -x grep -l "@mux/mux-player-react" {} \;

Length of output: 23


🏁 Script executed:

#!/bin/bash
# Show the full entry for @mux/mux-player-react in package.json
fd -t f "package.json" -x grep -A1 -B1 "@mux/mux-player-react" {} \;

Length of output: 111


🌐 Web query:

MuxPlayerRefAttributes React 19 compatibility @mux/mux-player-react

💡 Result:

Short answer: Yes — @mux/mux-player-react is implemented as a thin React wrapper around the mux-player web component (so runtime works with React 19), and the package ships TypeScript types for the player API. If you hit TypeScript/peer-dependency warnings with React 19, they’ll be from dev/peer deps (React/@types/react) rather than the web component itself; upgrading @mux/mux-player-react to the latest version or installing matching @types/react usually resolves them. Test by installing the latest @mux/mux-player-react and checking for peerDependency warnings; file an issue on Mux’s repo if type/peer-dep errors remain. [1][2][3]

Sources:

  • Mux docs — integrating & React component (shows @mux/mux-player-react is a React wrapper). [1]
  • Mux Player React API reference (types/props & guidance using the element). [2]
  • @mux/mux-player-react npm entry (package/releases info; useful for checking latest version/peer deps). [3]


---

🏁 Script executed:

```shell
#!/bin/bash
# Check if there are any type errors or comments around the ref usage
rg -n -B3 -A3 "playerRef.*useRef.*MuxPlayerRefAttributes" apps/web/src/components/

Length of output: 1064


🏁 Script executed:

#!/bin/bash
# Check @types/react version in apps/web
grep -E '"@types/react"' apps/web/package.json

Length of output: 30


🌐 Web query:

MuxPlayerRefAttributes useRef type definition mux-player-react

💡 Result:

Do you want the exact TypeScript type definition (copied from the package source) or an example of how to type a useRef for the MuxPlayer React component?



---

🏁 Script executed:

```shell
#!/bin/bash
# Check for any TypeScript diagnostic files or errors related to video-player.tsx
fd -t f "*.d.ts" -path "*mux-player-react*" 2>/dev/null | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for actual type definition or export of MuxPlayerRefAttributes
rg "export.*MuxPlayerRefAttributes|type MuxPlayerRefAttributes|interface MuxPlayerRefAttributes" --type ts --type tsx

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Check actual ref usage - does it call methods that would indicate the correct type
rg -n "playerRef\.current\." apps/web/src/components/video-player.tsx apps/web/src/components/video-modal.tsx

Length of output: 344


🌐 Web query:

@mux/mux-player-react MuxPlayerRefAttributes API documentation

💡 Result:

I couldn't find a public TypeScript symbol named "MuxPlayerRefAttributes". The React package documents that the ref for is a React ref to the underlying MuxPlayerElement (the web-component instance) and the full React API reference is in the Mux docs. See:

  • Mux Player React API reference (shows ref is a React ref to the underlying MuxPlayerElement). [1]
  • Mux Player API / repo (web components + React wrapper live in the muxinc/elements codebase). [2]

If you want the exact TypeScript interface exported by the package, I can:

  • fetch the package's type definitions from the @mux/mux-player-react npm package, or
  • open the muxinc/elements GitHub repo and look for the exported types (MuxPlayerRef*), and paste the interface here — which would show the available methods/properties on the ref. Which option do you prefer?

Sources:
[1] Mux Player React API Reference — Mux Docs.
[2] muxinc/elements GitHub (Mux Player + mux-player-react).



---

🌐 Web query:

```web
MuxPlayerElement ref type @mux/mux-player-react React useRef

💡 Result:

Use the web component instance type from @mux/mux-player and give that to useRef. Example:

import type MuxPlayerElement from '@mux/mux-player';
import MuxPlayer from '@mux/mux-player-react';
import { useRef } from 'react';

const playerRef = useRef<MuxPlayerElement | null>(null);

This matches the docs: the React component's ref is the underlying MuxPlayerElement. [1][2]

Sources:
[1] Mux Player React API reference (ref is a React ref to MuxPlayerElement).
[2] Mux blog/docs example importing type MuxPlayerElement from '@mux/mux-player'.


🏁 Script executed:

#!/bin/bash
# Check if MuxPlayerElement is imported anywhere
rg -n "MuxPlayerElement" apps/web/src/components/

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Show complete imports for both files
rg -n "^import" apps/web/src/components/video-player.tsx apps/web/src/components/video-modal.tsx

Length of output: 664


Replace MuxPlayerRefAttributes with MuxPlayerElement from the correct package.

The current type is undocumented and not part of the official Mux API. Per Mux documentation, the ref should be typed as MuxPlayerElement imported from '@mux/mux-player'.

Update both files:

  • apps/web/src/components/video-player.tsx (line 2 and 22)
  • apps/web/src/components/video-modal.tsx (line 3 and 13)

Change from:

import MuxPlayer, { type MuxPlayerRefAttributes } from "@mux/mux-player-react";
const playerRef = useRef<MuxPlayerRefAttributes>(null);

To:

import type MuxPlayerElement from "@mux/mux-player";
import MuxPlayer from "@mux/mux-player-react";
const playerRef = useRef<MuxPlayerElement | null>(null);
🤖 Prompt for AI Agents
In apps/web/src/components/video-player.tsx (line 2 and 22) and
apps/web/src/components/video-modal.tsx (line 3 and 13), replace the
undocumented MuxPlayerRefAttributes usage with the official MuxPlayerElement
type: import the type MuxPlayerElement from '@mux/mux-player' while keeping the
default MuxPlayer import from '@mux/mux-player-react', and change playerRef to
use useRef<MuxPlayerElement | null>(null) so the ref is correctly typed per Mux
documentation.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
apps/web/src/routes/_view/index.tsx (2)

317-317: Fix grammar in heading text.

The phrase "works like charm" is missing the article "a". It should read "works like a charm".

Apply this diff:

-            <h2 className="text-3xl font-serif text-stone-600 mb-4">Hyprnote works like charm</h2>
+            <h2 className="text-3xl font-serif text-stone-600 mb-4">Hyprnote works like a charm</h2>

70-77: Security: Add missing rel attribute to external link.

The anchor tag uses target="_blank" but is missing the rel="noopener noreferrer" attribute, which is a security risk (tab-nabbing vulnerability).

Apply this diff:

               <a
                 className="decoration-dotted underline hover:text-stone-600 transition-all"
                 href="https://github.com/fastrepl/hyprnote"
                 target="_blank"
+                rel="noopener noreferrer"
               >
🧹 Nitpick comments (2)
apps/web/src/components/video-thumbnail.tsx (1)

17-17: Follow coding guidelines: use cn with an array.

The coding guidelines specify that when using cn, always pass an array of class segments.

As per coding guidelines

Apply this diff:

-    <div className={cn("relative w-full h-full overflow-hidden group cursor-pointer", className)} onClick={onPlay}>
+    <div className={cn(["relative w-full h-full overflow-hidden group cursor-pointer", className])} onClick={onPlay}>
apps/web/src/components/github-open-source.tsx (1)

8-8: Remove obsolete TODO comment.

The TODO indicates the curated list needs to be added, but the list is already populated with 12 avatar URLs (lines 9-21).

Apply this diff:

-  // TODO: Add your curated list of avatar URLs here
   "https://avatars.githubusercontent.com/u/61503739?v=4",
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5659139 and f99ecf3.

⛔ Files ignored due to path filters (11)
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/hyprnote/icon.png is excluded by !**/*.png
  • apps/web/public/hyprnote/logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote/signature.svg is excluded by !**/*.svg
  • apps/web/public/icons/yc_stone.svg is excluded by !**/*.svg
  • apps/web/public/patterns/paper.png is excluded by !**/*.png
  • apps/web/public/patterns/white_leather.png is excluded by !**/*.png
  • apps/web/public/static.gif is excluded by !**/*.gif
  • apps/web/public/team/john.png is excluded by !**/*.png
  • apps/web/public/team/yujong.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • apps/web/package.json (1 hunks)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/components/video-modal.tsx (1 hunks)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/downloads.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
  • apps/web/src/routes/auth.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/video-player.tsx
  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/routes/_view/downloads.tsx
  • apps/web/src/routes/auth.tsx
  • apps/web/src/components/video-modal.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-thumbnail.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-thumbnail.tsx
🧬 Code graph analysis (3)
apps/web/src/components/github-open-source.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/index.tsx (9)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (11-50)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (13-104)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (64-111)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (12-88)
apps/web/src/components/video-thumbnail.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 73-73: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (1)
apps/web/src/routes/_view/index.tsx (1)

21-22: LGTM: Clean state management for video modal.

The video expansion state is well-structured using useState to track which video is expanded, with a proper null check for the modal's isOpen prop.

rel="noopener noreferrer"
className={cn([
"group px-6 h-12 inline-flex items-center justify-center gap-2 text-base sm:text-lg",
"bg-linear-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix invalid Tailwind utility class.

bg-linear-to-t is not a valid Tailwind class. The correct class for a top-to-bottom gradient is bg-gradient-to-t.

Apply this diff:

-                "bg-linear-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
+                "bg-gradient-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"bg-linear-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
"bg-gradient-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
🤖 Prompt for AI Agents
In apps/web/src/components/github-open-source.tsx around line 91, the Tailwind
utility "bg-linear-to-t" is invalid; replace it with the correct gradient class
"bg-gradient-to-t" while keeping the rest of the class list intact so the line
becomes "bg-gradient-to-t from-neutral-800 to-neutral-700 text-white
rounded-full".

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
apps/web/src/routes/_view/index.tsx (2)

396-396: Copy fix (regression).

“works like charm” → “works like a charm”.

-            <h2 className="text-3xl font-serif text-stone-600 mb-4">Hyprnote works like charm</h2>
+            <h2 className="text-3xl font-serif text-stone-600 mb-4">Hyprnote works like a charm</h2>

160-166: Security: target="_blank" missing rel.

Add rel="noopener noreferrer" to prevent tab-nabbing.

-                  <a
+                  <a
                     className="decoration-dotted underline hover:text-stone-600 transition-all"
                     href="https://github.com/fastrepl/hyprnote"
                     target="_blank"
+                    rel="noopener noreferrer"
                   >
🧹 Nitpick comments (7)
apps/web/src/routes/_view/route.tsx (1)

28-31: Avoid CLS: add intrinsic size on logo img.

Provide width/height (or sizes via CSS) and decoding for better LCP.

-  <img src="/hyprnote/logo.svg" alt="Hyprnote" className="h-6" />
+  <img
+    src="/hyprnote/logo.svg"
+    alt="Hyprnote"
+    className="h-6"
+    width="96"
+    height="24"
+    decoding="async"
+  />
apps/web/src/components/logo-cloud.tsx (2)

17-21: Follow cn guideline: always pass an array.

Update for consistency/readability.

-      className={cn(
-        "flex items-center justify-center bg-transparent px-4 py-8 md:p-8 hover:bg-neutral-50 transition-colors",
-        className,
-      )}
+      className={cn([
+        "flex items-center justify-center bg-transparent px-4 py-8 md:p-8",
+        "hover:bg-neutral-50 transition-colors",
+        className,
+      ])}

24-29: Fix img dimensions and lazy-load static logos.

Avoid invalid "auto" width/height; either omit or use CSS, and add loading/decoding.

-      <img
+      <img
         alt={logo.alt}
         className="pointer-events-none h-5 select-none md:h-6"
-        height={logo.height || "auto"}
+        height={logo.height ?? undefined}
         src={logo.src}
-        width={logo.width || "auto"}
+        width={logo.width ?? undefined}
+        loading="lazy"
+        decoding="async"
       />
apps/web/src/routes/_view/index.tsx (4)

406-415: Remove unknown/duplicate scrollbar class.

Use the new .scrollbar-none utility; drop scrollbar-hide to avoid dead CSS.

-              className="overflow-x-auto snap-x snap-mandatory scrollbar-hide scrollbar-none -mx-4"
+              className="overflow-x-auto snap-x snap-mandatory scrollbar-none -mx-4"
-              className="overflow-x-auto scrollbar-none snap-x snap-mandatory scrollbar-hide -mx-4"
+              className="overflow-x-auto scrollbar-none snap-x snap-mandatory -mx-4"

Also applies to: 584-593


123-129: cn usage: prefer array form per guidelines.

Switch to cn([...]) and group logically.

-          <div
-            className={cn(
-              "flex items-center justify-center gap-2 text-center",
-              "bg-stone-50/70 border-b border-stone-100",
-              "py-3 px-4",
-              "font-serif text-sm text-stone-700",
-              "hover:bg-stone-50 transition-all",
-            )}
-          >
+          <div
+            className={cn([
+              "flex items-center justify-center gap-2 text-center",
+              "bg-stone-50/70 border-b border-stone-100",
+              "py-3 px-4",
+              "font-serif text-sm text-stone-700",
+              "hover:bg-stone-50 transition-all",
+            ])}
+          >

460-466: Standardize cn([...]) across conditionals.

Adopt array form for consistency/readability.

-                  className={cn(
-                    "h-1 rounded-full transition-all cursor-pointer",
-                    selectedFeature === index
-                      ? "w-8 bg-stone-600"
-                      : "w-8 bg-neutral-300 hover:bg-neutral-400",
-                  )}
+                  className={cn([
+                    "h-1 rounded-full transition-all cursor-pointer",
+                    selectedFeature === index ? "w-8 bg-stone-600" : "w-8 bg-neutral-300 hover:bg-neutral-400",
+                  ])}
-                    className={cn(
-                      "h-1 rounded-full transition-all cursor-pointer",
-                      selectedDetail === index
-                        ? "w-8 bg-stone-600"
-                        : "w-8 bg-neutral-300 hover:bg-neutral-400",
-                    )}
+                    className={cn([
+                      "h-1 rounded-full transition-all cursor-pointer",
+                      selectedDetail === index ? "w-8 bg-stone-600" : "w-8 bg-neutral-300 hover:bg-neutral-400",
+                    ])}
-                    className={cn(
-                      "p-6 cursor-pointer transition-colors",
-                      index < detailsFeatures.length - 1 && "border-b border-neutral-100",
-                      "hover:bg-neutral-50",
-                    )}
+                    className={cn([
+                      "p-6 cursor-pointer transition-colors",
+                      index < detailsFeatures.length - 1 && "border-b border-neutral-100",
+                      "hover:bg-neutral-50",
+                    ])}

Also applies to: 631-635, 695-700


764-773: Lazy-load below-the-fold images.

Save bandwidth and improve LCP by marking non-critical images as lazy with async decoding.

-  <img
+  <img
     src="/team/john.png"
     alt="John Jeong"
     className="size-8 rounded-full object-cover border border-neutral-200"
+    loading="lazy"
+    decoding="async"
   />
-  <img
+  <img
     src="/hyprnote/signature.svg"
     alt="Hyprnote Signature"
     className="w-32 h-auto opacity-80"
+    loading="lazy"
+    decoding="async"
   />
-  <img
+  <img
     src="/hyprnote/icon.png"
     alt="Hyprnote"
     className="size-36 mx-auto rounded-[40px] border border-neutral-100"
+    loading="lazy"
+    decoding="async"
   />

Also applies to: 786-789, 801-804

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f99ecf3 and c2de687.

⛔ Files ignored due to path filters (1)
  • apps/web/public/icons/adobe.svg is excluded by !**/*.svg
📒 Files selected for processing (7)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/components/logo-cloud.tsx (1 hunks)
  • apps/web/src/components/social-card.tsx (1 hunks)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
  • apps/web/src/routes/_view/route.tsx (4 hunks)
  • apps/web/src/styles.css (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/components/social-card.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/components/video-player.tsx
  • apps/web/src/components/github-open-source.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/logo-cloud.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/logo-cloud.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/index.tsx (9)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (11-50)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (13-104)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (64-111)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (12-88)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 162-162: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (4)
apps/web/src/styles.css (1)

4-8: Breakpoint var looks good.

--breakpoint-laptop matches usage of laptop: classes in the app.

apps/web/src/routes/_view/route.tsx (2)

23-24: Container padding breakpoint change LGTM.


98-111: Footer container breakpoint change LGTM.

apps/web/src/routes/_view/index.tsx (1)

1-15: The pnpm -r typecheck command failed in the sandbox because the build environment lacks dependencies (node_modules, vite). This is expected in the sandbox and does not reflect the actual code quality.

The review comment is requesting a manual verification step that must be performed in your local development environment.

Run pnpm -r typecheck locally to verify no type regressions before merging.

Ensure the command completes successfully without errors in your development environment.

Comment on lines +10 to +18
@layer utilities {
.scrollbar-none {
-ms-overflow-style: none;
scrollbar-width: none;
}

.scrollbar-none::-webkit-scrollbar {
display: none;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Scrollbar utility: consistent usage across the app.

You introduced .scrollbar-none here; elsewhere the code still uses scrollbar-hide. Standardize on one (prefer .scrollbar-none) to avoid dead classes.

Search and replace leftovers:


🏁 Script executed:

#!/bin/bash
rg -n "scrollbar-hide|scrollbar-none" apps/web/src | sed -n '1,200p'

Length of output: 527


I'll verify whether .scrollbar-hide is defined in the codebase to determine if it's dead code or a necessary class.


🏁 Script executed:

#!/bin/bash
# Search for scrollbar-hide class definition in CSS files
rg -n "\.scrollbar-hide\s*[:{]" apps/web/src --type=css

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if scrollbar-hide appears in any CSS or style files
fd -e css -e scss -e sass | xargs grep -l "scrollbar-hide" 2>/dev/null | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Get full context for the lines using both scrollbar classes
rg -n -B2 -A2 "scrollbar-hide scrollbar-none|scrollbar-none.*scrollbar-hide" apps/web/src

Length of output: 889


Let me search for all occurrences of scrollbar-hide to get a complete picture:


🏁 Script executed:

#!/bin/bash
# Find all scrollbar-hide usages in the entire codebase
rg -n "scrollbar-hide" apps/web/src

Length of output: 281


Remove dead .scrollbar-hide class references in routes/_view/index.tsx.

.scrollbar-hide is never defined in the CSS but is used at lines 407 and 585 alongside .scrollbar-none. Since .scrollbar-hide has no CSS definition and line 646 demonstrates .scrollbar-none works alone, remove all scrollbar-hide references from the className strings.

🤖 Prompt for AI Agents
In apps/web/src/routes/_view/index.tsx at lines 407 and 585, remove the unused
"scrollbar-hide" token from the className strings (leave ".scrollbar-none"
intact) because ".scrollbar-hide" is not defined in apps/web/src/styles.css;
search the file for any other "scrollbar-hide" occurrences and delete them so
only the working ".scrollbar-none" remains in className lists, then run a quick
dev build to ensure no className typos remain.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
apps/web/src/routes/_view/index.tsx (1)

160-167: Add missing rel attribute for security.

The external link uses target="_blank" without rel attributes, which can expose the page to tab-nabbing attacks.

Apply this diff:

                  <a
                    className="decoration-dotted underline hover:text-stone-600 transition-all"
                    href="https://github.com/fastrepl/hyprnote"
                    target="_blank"
+                   rel="noopener noreferrer"
                  >
🧹 Nitpick comments (2)
apps/web/src/styles.css (1)

7-8: Remove the unused --breakpoint-laptop breakpoint variable.

The variable is defined at line 7 but has no references anywhere in the codebase. Either use this breakpoint in media queries or remove it.

apps/web/src/routes/_view/index.tsx (1)

426-426: Consider implementing "Learn more" functionality.

Multiple instances throughout the file use window.location.href = "#" as a placeholder. Consider implementing the actual navigation or removing the button if not needed.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2de687 and abb7a14.

⛔ Files ignored due to path filters (12)
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/hyprnote/icon.png is excluded by !**/*.png
  • apps/web/public/hyprnote/logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote/signature.svg is excluded by !**/*.svg
  • apps/web/public/icons/adobe.svg is excluded by !**/*.svg
  • apps/web/public/icons/yc_stone.svg is excluded by !**/*.svg
  • apps/web/public/patterns/paper.png is excluded by !**/*.png
  • apps/web/public/patterns/white_leather.png is excluded by !**/*.png
  • apps/web/public/static.gif is excluded by !**/*.gif
  • apps/web/public/team/john.png is excluded by !**/*.png
  • apps/web/public/team/yujong.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • apps/web/package.json (1 hunks)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/components/join-waitlist-button.tsx (1 hunks)
  • apps/web/src/components/logo-cloud.tsx (1 hunks)
  • apps/web/src/components/social-card.tsx (1 hunks)
  • apps/web/src/components/video-modal.tsx (1 hunks)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/downloads.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
  • apps/web/src/routes/_view/route.tsx (2 hunks)
  • apps/web/src/routes/auth.tsx (3 hunks)
  • apps/web/src/styles.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/web/src/components/social-card.tsx
  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/package.json
  • apps/web/src/components/video-player.tsx
  • apps/web/src/components/video-modal.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/routes/auth.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/components/join-waitlist-button.tsx
  • apps/web/src/components/video-thumbnail.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/routes/_view/downloads.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/components/join-waitlist-button.tsx
  • apps/web/src/components/video-thumbnail.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/routes/_view/downloads.tsx
🧬 Code graph analysis (3)
apps/web/src/components/join-waitlist-button.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/video-thumbnail.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/index.tsx (10)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/join-waitlist-button.tsx (1)
  • JoinWaitlistButton (3-33)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (11-50)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (13-104)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (64-111)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (12-88)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 163-163: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (5)
apps/web/src/styles.css (1)

10-19: Cross-browser scrollbar hiding implementation looks solid.

This utility properly addresses the earlier issue regarding the undefined .scrollbar-hide class. The implementation covers IE/Edge (-ms-overflow-style), Firefox (scrollbar-width), and WebKit browsers (::-webkit-scrollbar pseudo-element), which are the standard requirements for cross-browser scrollbar hiding.

apps/web/src/routes/_view/downloads.tsx (1)

61-61: LGTM!

The border color adjustment to a lighter shade aligns with the overall UI refresh in this PR.

apps/web/src/components/join-waitlist-button.tsx (1)

1-33: LGTM!

The component follows coding guidelines correctly, using cn with array syntax and proper class grouping. The external link includes appropriate security attributes.

apps/web/src/routes/_view/route.tsx (1)

23-74: LGTM!

The header and footer updates are well-implemented:

  • Breakpoint adjustments to laptop:px-0 provide better control for wider layouts
  • Logo image includes proper alt text
  • Navigation links have enhanced transitions with underline effects
  • External waitlist link includes proper security attributes
apps/web/src/components/logo-cloud.tsx (1)

18-25: LGTM!

The styling updates improve the visual presentation:

  • Removing cursor-pointer is appropriate for non-interactive elements
  • Logo size increase enhances visibility

@ComputelessComputer ComputelessComputer force-pushed the jj-branch-29 branch 2 times, most recently from 3a5150b to 3183eb8 Compare October 28, 2025 09:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (4)
apps/web/src/components/video-modal.tsx (2)

16-22: Add muted attribute for reliable autoplay.

Autoplay will be blocked by browsers unless the video is muted. Add muted to the MuxPlayer props to ensure consistent autoplay behavior across browsers.

Apply this diff:

       <MuxPlayer
         ref={playerRef}
         playbackId={playbackId}
         autoPlay
+        muted
         loop

Also applies to: 77-77


35-44: Preserve previous body overflow value.

The current implementation always resets document.body.style.overflow to an empty string, which may clobber the page's previous overflow setting.

Apply this diff:

   useEffect(() => {
+    const prev = document.body.style.overflow;
     if (isOpen) {
       document.body.style.overflow = "hidden";
     } else {
-      document.body.style.overflow = "";
+      document.body.style.overflow = prev;
     }
     return () => {
-      document.body.style.overflow = "";
+      document.body.style.overflow = prev;
     };
   }, [isOpen]);
apps/web/src/components/github-open-source.tsx (1)

96-96: Fix invalid Tailwind gradient class.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

-          "bg-linear-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
+          "bg-gradient-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
apps/web/src/routes/_view/index.tsx (1)

160-167: Add missing rel attribute for security.

The external link uses target="_blank" without rel attributes, which is a security risk (tab-nabbing).

Apply this diff:

                 <a
                   className="decoration-dotted underline hover:text-stone-600 transition-all"
                   href="https://github.com/fastrepl/hyprnote"
                   target="_blank"
+                  rel="noopener noreferrer"
                 >
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abb7a14 and 3183eb8.

⛔ Files ignored due to path filters (13)
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/hyprnote/icon.png is excluded by !**/*.png
  • apps/web/public/hyprnote/logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote/signature.svg is excluded by !**/*.svg
  • apps/web/public/icons/adobe.svg is excluded by !**/*.svg
  • apps/web/public/icons/yc_stone.svg is excluded by !**/*.svg
  • apps/web/public/patterns/paper.png is excluded by !**/*.png
  • apps/web/public/patterns/white_leather.png is excluded by !**/*.png
  • apps/web/public/static.gif is excluded by !**/*.gif
  • apps/web/public/team/john.png is excluded by !**/*.png
  • apps/web/public/team/yujong.png is excluded by !**/*.png
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • apps/web/package.json (1 hunks)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/components/join-waitlist-button.tsx (1 hunks)
  • apps/web/src/components/logo-cloud.tsx (1 hunks)
  • apps/web/src/components/social-card.tsx (1 hunks)
  • apps/web/src/components/video-modal.tsx (1 hunks)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/downloads.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
  • apps/web/src/routes/auth.tsx (3 hunks)
  • apps/web/src/routes/cal.tsx (1 hunks)
  • apps/web/src/styles.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/web/package.json
  • apps/web/src/components/video-player.tsx
  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/components/video-thumbnail.tsx
  • apps/web/src/routes/auth.tsx
  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/routes/_view/downloads.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/cal.tsx
  • apps/web/src/components/social-card.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/components/join-waitlist-button.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-modal.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/cal.tsx
  • apps/web/src/components/social-card.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/components/join-waitlist-button.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-modal.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, always pass an array of class segments

Applied to files:

  • apps/web/src/routes/_view/index.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When many classNames have conditional logic in React components, use `cn` from `hypr/utils`

Applied to files:

  • apps/web/src/routes/_view/index.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, split entries by logical grouping for readability

Applied to files:

  • apps/web/src/routes/_view/index.tsx
🧬 Code graph analysis (5)
apps/web/src/routes/cal.tsx (2)
apps/web/src/routes/_view/route.tsx (1)
  • Route (3-6)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/github-open-source.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/join-waitlist-button.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/index.tsx (10)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/join-waitlist-button.tsx (1)
  • JoinWaitlistButton (3-33)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (11-50)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (13-104)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (138-314)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (12-88)
apps/web/src/components/video-modal.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 163-163: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (7)
apps/web/src/styles.css (2)

7-7: LGTM!

The custom breakpoint follows Tailwind's @theme convention and provides a reasonable laptop breakpoint at 1152px.


10-19: LGTM!

The .scrollbar-none utility is correctly implemented with cross-browser support for hiding scrollbars (IE/Edge, Firefox, and WebKit browsers).

apps/web/src/components/social-card.tsx (1)

78-78: LGTM!

The responsive text truncation (4 lines mobile, 15 lines desktop) improves the mobile UX while preserving full content on larger screens.

apps/web/src/routes/_view/route.tsx (2)

10-10: LGTM!

The layout updates consistently use the new laptop: breakpoint and add proper flex layout for a sticky footer pattern.

Also applies to: 23-23, 74-74


28-30: LGTM!

Replacing text branding with an SVG logo improves visual consistency and brand identity.

apps/web/src/routes/_view/index.tsx (2)

29-94: LGTM!

The feature data structures are well-organized with clear separation between main features and details. The comingSoon flags and consistent icon naming make the data easy to maintain.


96-112: LGTM!

The scroll helper functions correctly calculate scroll positions and sync with the indicators. The smooth scrolling behavior enhances UX.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (14)
apps/web/src/components/video-modal.tsx (4)

35-44: Preserve previous body overflow value.

The current implementation unconditionally resets overflow to "", which may clobber the page's previous state if it had a custom overflow setting.

Apply this diff:

   useEffect(() => {
+    const prev = document.body.style.overflow;
     if (isOpen) {
       document.body.style.overflow = "hidden";
     } else {
-      document.body.style.overflow = "";
+      document.body.style.overflow = prev;
     }
     return () => {
-      document.body.style.overflow = "";
+      document.body.style.overflow = prev;
     };
   }, [isOpen]);

3-3: Replace undocumented type with official MuxPlayerElement.

The MuxPlayerRefAttributes type is not part of the official Mux API. Per Mux documentation, use MuxPlayerElement from '@mux/mux-player'.

Apply this diff:

-import MuxPlayer, { type MuxPlayerRefAttributes } from "@mux/mux-player-react";
+import type MuxPlayerElement from "@mux/mux-player";
+import MuxPlayer from "@mux/mux-player-react";

And update line 13:

-  const playerRef = useRef<MuxPlayerRefAttributes>(null);
+  const playerRef = useRef<MuxPlayerElement | null>(null);

1-4: Add "use client" directive for SSR compatibility.

This component uses MuxPlayer (a web component) and DOM manipulation, requiring client-side rendering to avoid hydration mismatches.

Add this directive at the top of the file:

+"use client";
+
 import { cn } from "@hypr/utils";

74-84: Add muted attribute for reliable autoplay.

Autoplay without muted will be blocked by most browsers, especially Safari/iOS. Add the muted attribute to ensure consistent behavior.

Apply this diff:

         <MuxPlayer
           ref={playerRef}
           playbackId={playbackId}
           autoPlay
+          muted
+          playsInline
           loop
           accentColor="#78716c"
apps/web/src/components/join-waitlist-button.tsx (1)

11-11: Fix invalid Tailwind gradient class.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

-        "bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full",
+        "bg-gradient-to-t from-stone-600 to-stone-500 text-white rounded-full",
apps/web/src/components/video-player.tsx (2)

2-2: Replace undocumented type with official MuxPlayerElement.

The MuxPlayerRefAttributes type is not part of the official Mux API. Per Mux documentation, use MuxPlayerElement from '@mux/mux-player'.

Apply this diff:

-import MuxPlayer, { type MuxPlayerRefAttributes } from "@mux/mux-player-react";
+import type MuxPlayerElement from "@mux/mux-player";
+import MuxPlayer from "@mux/mux-player-react";

And update line 22:

-  const playerRef = useRef<MuxPlayerRefAttributes>(null);
+  const playerRef = useRef<MuxPlayerElement | null>(null);

1-4: Add "use client" directive for SSR compatibility.

Since this component uses MuxPlayer (a web component), it needs client-side rendering to avoid hydration mismatches in the SSR environment.

Add this directive at the top of the file:

+"use client";
+
 import { cn } from "@hypr/utils";
apps/web/src/routes/_view/index.tsx (7)

114-114: Fix invalid Tailwind gradient class.

bg-linear-to-b is not a valid Tailwind class. Use bg-gradient-to-b instead.

Apply this diff:

-    <main className="flex-1 bg-linear-to-b from-white via-stone-50/20 to-white min-h-screen">
+    <main className="flex-1 bg-gradient-to-b from-white via-stone-50/20 to-white min-h-screen">

142-142: Fix invalid Tailwind gradient class.

bg-linear-to-b is not a valid Tailwind class. Use bg-gradient-to-b instead.

Apply this diff:

-        <div className="bg-linear-to-b from-stone-50/30 to-stone-100/30">
+        <div className="bg-gradient-to-b from-stone-50/30 to-stone-100/30">

160-167: Add missing rel attribute for security.

External links with target="_blank" require rel="noopener noreferrer" to prevent tab-nabbing security issues.

Apply this diff:

                   <a
                     className="decoration-dotted underline hover:text-stone-600 transition-all"
                     href="https://github.com/fastrepl/hyprnote"
                     target="_blank"
+                    rel="noopener noreferrer"
                   >

408-408: Remove dead scrollbar-hide class.

The scrollbar-hide class is not defined in the CSS. Only scrollbar-none is needed.

Apply this diff:

-              className="overflow-x-auto snap-x snap-mandatory scrollbar-hide scrollbar-none -mx-4"
+              className="overflow-x-auto snap-x snap-mandatory scrollbar-none -mx-4"

586-586: Remove dead scrollbar-hide class.

The scrollbar-hide class is not defined in the CSS. Only scrollbar-none is needed.

Apply this diff:

-                className="overflow-x-auto scrollbar-none snap-x snap-mandatory scrollbar-hide -mx-4"
+                className="overflow-x-auto scrollbar-none snap-x snap-mandatory -mx-4"

798-798: Fix invalid Tailwind gradient class.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

-        <section className="py-16 border-t border-neutral-100 bg-linear-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
+        <section className="py-16 border-t border-neutral-100 bg-gradient-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">

124-131: Refactor cn usage to array syntax.

Per coding guidelines, cn should always receive an array of class segments.

Based on learnings

Apply this diff:

           <div
-            className={cn(
+            className={cn([
               "flex items-center justify-center gap-2 text-center",
               "bg-stone-50/70 border-b border-stone-100",
               "py-3 px-4",
               "font-serif text-sm text-stone-700",
               "hover:bg-stone-50 transition-all",
-            )}
+            ])}
           >
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3183eb8 and def95fe.

⛔ Files ignored due to path filters (13)
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/hyprnote/icon.png is excluded by !**/*.png
  • apps/web/public/hyprnote/logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote/signature.svg is excluded by !**/*.svg
  • apps/web/public/icons/adobe.svg is excluded by !**/*.svg
  • apps/web/public/icons/yc_stone.svg is excluded by !**/*.svg
  • apps/web/public/patterns/paper.png is excluded by !**/*.png
  • apps/web/public/patterns/white_leather.png is excluded by !**/*.png
  • apps/web/public/static.gif is excluded by !**/*.gif
  • apps/web/public/team/john.png is excluded by !**/*.png
  • apps/web/public/team/yujong.png is excluded by !**/*.png
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • apps/web/package.json (1 hunks)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/components/join-waitlist-button.tsx (1 hunks)
  • apps/web/src/components/logo-cloud.tsx (1 hunks)
  • apps/web/src/components/social-card.tsx (1 hunks)
  • apps/web/src/components/video-modal.tsx (1 hunks)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/downloads.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
  • apps/web/src/routes/auth.tsx (3 hunks)
  • apps/web/src/routes/cal.tsx (1 hunks)
  • apps/web/src/styles.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/routes/cal.tsx
  • apps/web/src/routes/_view/downloads.tsx
  • apps/web/src/components/social-card.tsx
  • apps/web/src/components/video-thumbnail.tsx
  • apps/web/src/components/github-open-source.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/auth.tsx
  • apps/web/src/components/video-player.tsx
  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/components/join-waitlist-button.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/video-modal.tsx
  • apps/web/src/routes/_view/index.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/auth.tsx
  • apps/web/src/components/video-player.tsx
  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/components/join-waitlist-button.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/video-modal.tsx
  • apps/web/src/routes/_view/index.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When many classNames have conditional logic in React components, use `cn` from `hypr/utils`

Applied to files:

  • apps/web/src/routes/auth.tsx
  • apps/web/src/routes/_view/index.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, always pass an array of class segments

Applied to files:

  • apps/web/src/routes/_view/index.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, split entries by logical grouping for readability

Applied to files:

  • apps/web/src/routes/_view/index.tsx
🧬 Code graph analysis (4)
apps/web/src/components/video-player.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/join-waitlist-button.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/video-modal.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/index.tsx (10)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/join-waitlist-button.tsx (1)
  • JoinWaitlistButton (3-33)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (11-50)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (13-104)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (138-314)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (12-88)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 163-163: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (3)
apps/web/src/components/logo-cloud.tsx (1)

25-25: LGTM: Logo size increase improves visibility.

The increased logo dimensions (h-5 and md:h-6) enhance visual presence without causing layout issues.

apps/web/src/styles.css (1)

7-18: LGTM! Well-structured CSS utilities.

The custom breakpoint and scrollbar utility are properly defined with comprehensive browser support.

apps/web/src/routes/auth.tsx (1)

1-229: LGTM! Clean layout refactor with proper cn usage.

The simplified container structure and updated branding assets improve the auth flow. The cn usage correctly follows the coding guidelines with array syntax.

Improve page layout with full-height container and
expand GitHub contributors list with usernames and
avatar URLs for better representation and
attribution of open-source contributors.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
apps/web/src/routes/_view/index.tsx (4)

804-804: Fix invalid Tailwind gradient class.

The class bg-linear-to-t is not valid Tailwind CSS. Use bg-gradient-to-t instead.

Apply this diff:

-        <section className="py-16 border-t border-neutral-100 bg-linear-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
+        <section className="py-16 border-t border-neutral-100 bg-gradient-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">

114-114: Fix invalid Tailwind gradient class.

The class bg-linear-to-b is not valid Tailwind CSS. Use bg-gradient-to-b instead.

Apply this diff:

-    <main className="flex-1 bg-linear-to-b from-white via-stone-50/20 to-white min-h-screen">
+    <main className="flex-1 bg-gradient-to-b from-white via-stone-50/20 to-white min-h-screen">

162-169: Security: Add missing rel attribute to prevent tab-nabbing.

The external link uses target="_blank" without rel="noopener noreferrer", which creates a security vulnerability.

Apply this diff:

                  <a
                    className="decoration-dotted underline hover:text-stone-600 transition-all"
                    href="https://github.com/fastrepl/hyprnote"
                    target="_blank"
+                   rel="noopener noreferrer"
                  >

144-144: Fix invalid Tailwind gradient class.

The class bg-linear-to-b is not valid Tailwind CSS. Use bg-gradient-to-b instead.

Apply this diff:

-        <div className="bg-linear-to-b from-stone-50/30 to-stone-100/30">
+        <div className="bg-gradient-to-b from-stone-50/30 to-stone-100/30">
🧹 Nitpick comments (1)
apps/web/src/routes/_view/index.tsx (1)

428-428: Address placeholder navigation callbacks.

Multiple instances of onLearnMore={() => window.location.href = "#"} appear throughout the file (lines 428, 484, 503, 522, 541, 606, 687, 730). These are no-ops that don't provide any functionality. Consider either removing the onLearnMore prop or implementing proper navigation.

Either remove the prop if not needed:

                            <VideoPlayer
                              playbackId={MUX_PLAYBACK_ID}
-                             onLearnMore={() => window.location.href = "#"}
                              onExpandVideo={() => setExpandedVideo(MUX_PLAYBACK_ID)}
                            />

Or implement proper navigation to a relevant section/page.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between def95fe and 80dbe58.

📒 Files selected for processing (3)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/components/video-player.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-thumbnail.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-thumbnail.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, always pass an array of class segments

Applied to files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-thumbnail.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When many classNames have conditional logic in React components, use `cn` from `hypr/utils`

Applied to files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-thumbnail.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, split entries by logical grouping for readability

Applied to files:

  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-thumbnail.tsx
🧬 Code graph analysis (2)
apps/web/src/routes/_view/index.tsx (9)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/join-waitlist-button.tsx (1)
  • JoinWaitlistButton (3-33)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (11-50)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (18-82)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (13-106)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (4-32)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (5-35)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (12-88)
apps/web/src/components/video-thumbnail.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 165-165: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (5)
apps/web/src/components/video-thumbnail.tsx (1)

1-50: LGTM! Component follows coding guidelines.

The VideoThumbnail component is well-implemented with proper use of cn array syntax, good accessibility (aria-label), and correct event handling (stopPropagation). The structure is clean and the MuxPlayer integration looks solid.

apps/web/src/routes/_view/index.tsx (4)

659-664: Refactor cn to use array syntax.

Per coding guidelines, cn must always receive an array of class segments.

As per coding guidelines

Apply this diff:

                         className={cn(
+                          [
                           "p-6 border-r border-neutral-100 last:border-r-0 min-w-[280px] text-left transition-colors",
                           selectedDetail === index
                             ? "bg-stone-50"
                             : "hover:bg-neutral-50",
+                          ]
                         )}

702-706: Refactor cn to use array syntax.

Per coding guidelines, cn must always receive an array of class segments.

As per coding guidelines

Apply this diff:

                     className={cn(
+                      [
                       "p-6 cursor-pointer transition-colors",
                       index < detailsFeatures.length - 1 && "border-b border-neutral-100",
                       "hover:bg-neutral-50",
+                      ]
                     )}

22-112: Well-structured component state and helpers.

The state management, data structures (mainFeatures, detailsFeatures), and scroll helper functions are well-organized and clean. The use of refs for scroll containers is appropriate.


828-832: VideoModal integration looks good.

The expandedVideo state is properly wired to the VideoModal component with correct props and state management.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (11)
apps/web/src/components/logo-cloud.tsx (1)

17-20: Follow coding guidelines: wrap cn arguments in an array.

The cn utility should be called with an array of class segments per the project's coding guidelines and retrieved learnings. This issue was already flagged in a previous review but remains unresolved.

As per coding guidelines.

Apply this diff:

-      className={cn(
-        "flex items-center justify-center bg-transparent px-4 py-8 md:p-8 hover:bg-neutral-50 transition-colors",
-        className,
-      )}
+      className={cn([
+        "flex items-center justify-center bg-transparent px-4 py-8 md:p-8 hover:bg-neutral-50 transition-colors",
+        className,
+      ])}
apps/web/src/styles.css (1)

10-18: Scrollbar utility: add WebKit width/height for robustness + a11y fallback.

Pattern is solid. To cover WebKit cases that ignore display: none, also set zero width/height on the pseudo-element. Consider an a11y fallback for high-contrast users.

Apply within this block:

   .scrollbar-none::-webkit-scrollbar {
-    display: none;
+    display: none;
+    width: 0;
+    height: 0;
   }

Optionally add outside this block (same file):

/* Revert utility when needed (nested components) */
@layer utilities {
  .scrollbar-auto {
    -ms-overflow-style: auto;
    scrollbar-width: auto;
  }
}

/* Respect system high-contrast settings */
@media (forced-colors: active) {
  .scrollbar-none {
    -ms-overflow-style: auto;
    scrollbar-width: auto;
  }
}

Also, continue removing any .scrollbar-hide remnants across the app to standardize on .scrollbar-none.

apps/web/src/components/video-modal.tsx (3)

35-44: Preserve previous body overflow value.

Already flagged in past review; capture and restore original overflow state.


3-3: Use MuxPlayerElement type.

Already flagged in past review; import from @mux/mux-player.

Also applies to: 13-13


16-23: Add muted attribute for reliable autoplay.

Already flagged in past review; autoplay requires muted for Safari/iOS.

apps/web/src/routes/_view/route.tsx (1)

57-57: Fix invalid Tailwind gradient class.

bg-linear-to-t is not valid; use bg-gradient-to-t.

apps/web/src/components/video-player.tsx (1)

2-2: Use MuxPlayerElement instead of MuxPlayerRefAttributes.

Per past review and Mux documentation, import MuxPlayerElement from @mux/mux-player.

Also applies to: 22-22

apps/web/src/routes/_view/index.tsx (4)

114-114: Fix invalid gradient class.

Use bg-gradient-to-b instead of bg-linear-to-b.


165-166: Add rel="noopener noreferrer" to external link.

Security: prevent tab-nabbing on target="_blank" links.


659-664: Refactor cn to use array syntax.

Per coding guidelines, wrap class strings in an array.

Based on learnings


702-706: Refactor cn to use array syntax.

Per coding guidelines, wrap arguments in an array.

Based on learnings

🧹 Nitpick comments (1)
apps/web/src/components/social-card.tsx (1)

78-78: Good responsive improvement; consider removing redundant class.

The responsive text clamping (4 lines on mobile, 15 on larger screens) improves readability on smaller viewports. However, overflow-hidden is redundant since Tailwind's line-clamp-* utilities already include it.

Apply this diff to remove the redundant class:

-        <p className="text-neutral-700 leading-relaxed line-clamp-4 md:line-clamp-15 overflow-hidden">{body}</p>
+        <p className="text-neutral-700 leading-relaxed line-clamp-4 md:line-clamp-15">{body}</p>
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80dbe58 and 3556cd7.

⛔ Files ignored due to path filters (13)
  • apps/web/public/favicon.ico is excluded by !**/*.ico
  • apps/web/public/hyprnote/icon.png is excluded by !**/*.png
  • apps/web/public/hyprnote/logo.svg is excluded by !**/*.svg
  • apps/web/public/hyprnote/signature.svg is excluded by !**/*.svg
  • apps/web/public/icons/adobe.svg is excluded by !**/*.svg
  • apps/web/public/icons/yc_stone.svg is excluded by !**/*.svg
  • apps/web/public/patterns/paper.png is excluded by !**/*.png
  • apps/web/public/patterns/white_leather.png is excluded by !**/*.png
  • apps/web/public/static.gif is excluded by !**/*.gif
  • apps/web/public/team/john.png is excluded by !**/*.png
  • apps/web/public/team/yujong.png is excluded by !**/*.png
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (15)
  • apps/web/package.json (1 hunks)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/components/join-waitlist-button.tsx (1 hunks)
  • apps/web/src/components/logo-cloud.tsx (1 hunks)
  • apps/web/src/components/social-card.tsx (1 hunks)
  • apps/web/src/components/video-modal.tsx (1 hunks)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/downloads.tsx (1 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
  • apps/web/src/routes/_view/route.tsx (3 hunks)
  • apps/web/src/routes/auth.tsx (3 hunks)
  • apps/web/src/routes/cal.tsx (1 hunks)
  • apps/web/src/styles.css (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • apps/web/src/components/join-waitlist-button.tsx
  • apps/web/src/routes/auth.tsx
  • apps/web/src/routes/cal.tsx
  • apps/web/src/components/video-thumbnail.tsx
  • apps/web/package.json
  • apps/web/src/components/github-open-source.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/routes/_view/downloads.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/components/social-card.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-player.tsx
  • apps/web/src/components/video-modal.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/routes/_view/downloads.tsx
  • apps/web/src/routes/_view/route.tsx
  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/components/social-card.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/video-player.tsx
  • apps/web/src/components/video-modal.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, always pass an array of class segments

Applied to files:

  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/routes/_view/index.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When many classNames have conditional logic in React components, use `cn` from `hypr/utils`

Applied to files:

  • apps/web/src/components/logo-cloud.tsx
  • apps/web/src/routes/_view/index.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, split entries by logical grouping for readability

Applied to files:

  • apps/web/src/routes/_view/index.tsx
🧬 Code graph analysis (3)
apps/web/src/routes/_view/index.tsx (6)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/join-waitlist-button.tsx (1)
  • JoinWaitlistButton (3-33)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (11-50)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (13-106)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (138-314)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (12-88)
apps/web/src/components/video-player.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/video-modal.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 165-165: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🔇 Additional comments (9)
apps/web/src/components/logo-cloud.tsx (1)

18-18: LGTM: appropriate visual adjustments.

The removal of cursor-pointer is correct since the logos are non-interactive, and the logo size increase (h-4→h-5, md:h-5→md:h-6) provides better visual balance. These changes align well with the broader UI polish in this PR.

Also applies to: 25-25

apps/web/src/styles.css (1)

7-8: Breakpoint token looks good; verify consumer classes exist.

Defining --breakpoint-laptop: 72rem will generate a laptop: screen in Tailwind v4. Please confirm there are real usages and no overlap/conflicts with nearby screens (e.g., lg, xl). If unused, consider deferring to avoid token drift.

apps/web/src/routes/_view/downloads.tsx (1)

61-61: LGTM - Consistent styling refinement.

The border color change aligns with the broader styling harmonization across the PR.

apps/web/src/routes/_view/app/account.tsx (1)

159-159: LGTM - Consistent border styling updates.

The border color updates across both settings cards align with the broader UI styling harmonization in this PR.

Also applies to: 167-167, 181-181, 189-189

apps/web/src/components/video-player.tsx (1)

39-105: LGTM - Well-structured video player component.

The hover-driven autoplay, overlay controls, and cn array syntax usage all follow best practices.

apps/web/src/routes/_view/index.tsx (4)

22-27: LGTM - Well-structured state management and feature definitions.

The video state, scroll refs, feature arrays, and scroll helpers are cleanly organized and support the interactive UI patterns well.

Also applies to: 29-112


124-132: LGTM - Correct cn array syntax.

Properly uses array syntax with logical groupings.

Based on learnings


463-471: LGTM - Correct cn array syntax with conditional.

Properly structured with array syntax.

Based on learnings


635-643: LGTM - Correct cn array syntax.

Follows guidelines with array syntax.

Based on learnings

Decompose the booking page into smaller, reusable components
for improved code organization and readability. Split the
existing monolithic component into separate Header,
BookingButton, and FAQ sections, making the code more
maintainable and easier to understand.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

♻️ Duplicate comments (6)
apps/web/src/components/video-modal.tsx (2)

36-45: Preserve previous body overflow value.

The current implementation always resets document.body.style.overflow to an empty string, which may clobber the page's previous overflow setting.

Apply this diff:

   useEffect(() => {
+    const prev = document.body.style.overflow;
     if (isOpen) {
       document.body.style.overflow = "hidden";
     } else {
-      document.body.style.overflow = "";
+      document.body.style.overflow = prev;
     }
     return () => {
-      document.body.style.overflow = "";
+      document.body.style.overflow = prev;
     };
   }, [isOpen]);

17-23: Autoplay may fail without muted attribute.

Browsers often block autoplay unless the video is muted. Add muted and playsInline to ensure reliable autoplay across all browsers, especially Safari/iOS.

Apply this diff to the useEffect:

   useEffect(() => {
     if (isOpen && playerRef.current) {
+      playerRef.current.muted = true;
       playerRef.current.play().catch(() => {
         // Ignore autoplay errors
       });
     }
   }, [isOpen]);

And update the MuxPlayer props:

         <MuxPlayer
           ref={playerRef}
           playbackId={playbackId}
           autoPlay
+          muted
+          playsInline
           loop
apps/web/src/components/github-open-source.tsx (1)

96-96: Fix invalid Tailwind gradient class.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

-          "bg-linear-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
+          "bg-gradient-to-t from-neutral-800 to-neutral-700 text-white rounded-full",
apps/web/src/routes/_view/index.tsx (3)

116-116: Fix invalid Tailwind gradient class.

bg-linear-to-b is not a valid Tailwind class. Use bg-gradient-to-b instead.

Apply this diff:

-    <main className="flex-1 bg-linear-to-b from-white via-stone-50/20 to-white min-h-screen">
+    <main className="flex-1 bg-gradient-to-b from-white via-stone-50/20 to-white min-h-screen">

180-180: Fix invalid Tailwind gradient class.

bg-linear-to-b is not a valid Tailwind class. Use bg-gradient-to-b instead.

Apply this diff:

-    <div className="bg-linear-to-b from-stone-50/30 to-stone-100/30">
+    <div className="bg-gradient-to-b from-stone-50/30 to-stone-100/30">

943-943: Fix invalid Tailwind gradient class.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

-    <section className="py-16 border-t border-neutral-100 bg-linear-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
+    <section className="py-16 border-t border-neutral-100 bg-gradient-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
🧹 Nitpick comments (1)
apps/web/src/components/social-card.tsx (1)

77-77: Nice responsive design improvement!

The mobile-first line-clamping (4 lines on small screens, 15 on medium+) improves readability on smaller devices.

Optional: The overflow-hidden class is redundant since Tailwind's line-clamp-* utilities already apply overflow: hidden. You can safely remove it:

-        <p className="text-neutral-700 leading-relaxed line-clamp-4 md:line-clamp-15 overflow-hidden">{body}</p>
+        <p className="text-neutral-700 leading-relaxed line-clamp-4 md:line-clamp-15">{body}</p>
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3556cd7 and cb767a6.

⛔ Files ignored due to path filters (1)
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
📒 Files selected for processing (15)
  • apps/web/src/components/download-button.tsx (1 hunks)
  • apps/web/src/components/github-open-source.tsx (1 hunks)
  • apps/web/src/components/github-stars.tsx (1 hunks)
  • apps/web/src/components/not-found.tsx (1 hunks)
  • apps/web/src/components/social-card.tsx (1 hunks)
  • apps/web/src/components/testimonial-card.tsx (0 hunks)
  • apps/web/src/components/video-modal.tsx (1 hunks)
  • apps/web/src/components/video-player.tsx (1 hunks)
  • apps/web/src/components/video-thumbnail.tsx (1 hunks)
  • apps/web/src/routes/_view/app/account.tsx (2 hunks)
  • apps/web/src/routes/_view/download.tsx (1 hunks)
  • apps/web/src/routes/_view/downloads.tsx (0 hunks)
  • apps/web/src/routes/_view/index.tsx (5 hunks)
  • apps/web/src/routes/_view/pricing.tsx (1 hunks)
  • apps/web/src/routes/cal.tsx (1 hunks)
💤 Files with no reviewable changes (2)
  • apps/web/src/components/testimonial-card.tsx
  • apps/web/src/routes/_view/downloads.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/web/src/components/github-stars.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/web/src/routes/cal.tsx
  • apps/web/src/routes/_view/app/account.tsx
  • apps/web/src/components/video-player.tsx
  • apps/web/src/components/video-thumbnail.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

After a substantial amount of TypeScript changes, run pnpm -r typecheck

Files:

  • apps/web/src/routes/_view/download.tsx
  • apps/web/src/routes/_view/pricing.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/components/not-found.tsx
  • apps/web/src/components/download-button.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/social-card.tsx
  • apps/web/src/components/video-modal.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/simple.mdc)

**/*.{tsx,jsx}: When many classNames have conditional logic in React components, use cn from @hypr/utils
When using cn, always pass an array of class segments
When using cn, split entries by logical grouping for readability
Use motion/react instead of framer-motion

Files:

  • apps/web/src/routes/_view/download.tsx
  • apps/web/src/routes/_view/pricing.tsx
  • apps/web/src/components/github-open-source.tsx
  • apps/web/src/components/not-found.tsx
  • apps/web/src/components/download-button.tsx
  • apps/web/src/routes/_view/index.tsx
  • apps/web/src/components/social-card.tsx
  • apps/web/src/components/video-modal.tsx
🧠 Learnings (3)
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, always pass an array of class segments

Applied to files:

  • apps/web/src/routes/_view/index.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When many classNames have conditional logic in React components, use `cn` from `hypr/utils`

Applied to files:

  • apps/web/src/routes/_view/index.tsx
📚 Learning: 2025-10-26T00:28:22.016Z
Learnt from: CR
PR: fastrepl/hyprnote#0
File: .cursor/rules/simple.mdc:0-0
Timestamp: 2025-10-26T00:28:22.016Z
Learning: Applies to **/*.{tsx,jsx} : When using `cn`, split entries by logical grouping for readability

Applied to files:

  • apps/web/src/routes/_view/index.tsx
🧬 Code graph analysis (5)
apps/web/src/routes/_view/download.tsx (2)
apps/web/src/routes/_view/index.tsx (1)
  • Route (86-88)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/pricing.tsx (3)
apps/web/src/routes/_view/index.tsx (1)
  • Route (86-88)
apps/web/src/routes/_view/route.tsx (1)
  • Route (3-6)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/github-open-source.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/index.tsx (10)
apps/web/src/components/github-open-source.tsx (1)
  • GitHubOpenSource (138-281)
apps/web/src/components/video-modal.tsx (1)
  • VideoModal (13-89)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/components/join-waitlist-button.tsx (1)
  • JoinWaitlistButton (3-33)
apps/web/src/components/video-thumbnail.tsx (1)
  • VideoThumbnail (12-49)
apps/web/src/components/logo-cloud.tsx (1)
  • LogoCloud (35-123)
apps/web/src/components/social-card.tsx (1)
  • SocialCard (17-81)
apps/web/src/components/video-player.tsx (1)
  • VideoPlayer (14-104)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (5-33)
apps/web/src/components/github-stars.tsx (1)
  • GithubStars (6-36)
apps/web/src/components/video-modal.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
🪛 Biome (2.1.2)
apps/web/src/routes/_view/index.tsx

[error] 200-200: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🪛 GitHub Actions: .github/workflows/fmt.yaml
apps/web/src/routes/_view/pricing.tsx

[error] 165-191: dprint formatting check failed. Found 1 not formatted file. Command: '/.dprint/bin/dprint check --config dprint.json'. To fix, run '/.dprint/bin/dprint fmt --config dprint.json' to format.

🔇 Additional comments (13)
apps/web/src/components/not-found.tsx (1)

75-75: LGTM! Route update is consistent with the refactoring.

The change from /downloads to /download aligns with the removal of the old downloads route and the introduction of the new download page.

apps/web/src/components/download-button.tsx (1)

2-8: LGTM! Route update is consistent.

The change to /download aligns with the broader route reorganization in this PR.

apps/web/src/routes/_view/download.tsx (1)

13-26: LGTM! Informative banner for users.

The information banner clearly communicates platform-specific features and limitations.

apps/web/src/components/github-open-source.tsx (2)

7-60: LGTM! Well-structured contributor data.

The curated profiles list is comprehensive and properly formatted for the open-source showcase.


138-281: LGTM! Well-structured responsive layout.

The component properly handles mobile and desktop layouts with appropriate breakpoints and maintains visual consistency across viewports.

apps/web/src/routes/_view/pricing.tsx (1)

10-82: LGTM! Well-structured pricing data.

The pricing plans and features are clearly defined with appropriate metadata (coming soon flags, tooltips, etc.).

apps/web/src/components/video-modal.tsx (1)

51-88: LGTM! Well-structured modal with accessibility.

The modal includes proper accessibility attributes, event handling, and responsive layout.

apps/web/src/routes/_view/index.tsx (6)

1-84: LGTM! Well-organized imports and feature data.

The new imports and data structures for features are comprehensive and properly typed.


97-113: LGTM! Well-implemented scroll helpers.

The scroll functions properly handle smooth scrolling to feature and detail sections.


482-563: LGTM! Well-implemented mobile carousel.

The features carousel includes proper scroll tracking, snap points, and navigation indicators.


709-780: LGTM! Well-implemented details carousel.

The details carousel mirrors the features carousel with consistent behavior and UI patterns.


565-663: LGTM! Well-structured desktop feature grid.

The desktop grid layout properly showcases features with appropriate spacing and video integration.


879-939: LGTM! Well-crafted manifesto section.

The manifesto presentation effectively communicates the company's values with appropriate styling and layout.


function Component() {
return (
<div className="bg-linear-to-b from-white via-blue-50/20 to-white min-h-screen">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix invalid Tailwind gradient class.

bg-linear-to-b is not a valid Tailwind class. Use bg-gradient-to-b instead.

Apply this diff:

-    <div className="bg-linear-to-b from-white via-blue-50/20 to-white min-h-screen">
+    <div className="bg-gradient-to-b from-white via-blue-50/20 to-white min-h-screen">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className="bg-linear-to-b from-white via-blue-50/20 to-white min-h-screen">
<div className="bg-gradient-to-b from-white via-blue-50/20 to-white min-h-screen">
🤖 Prompt for AI Agents
In apps/web/src/routes/_view/download.tsx around line 12, the Tailwind class
"bg-linear-to-b" is invalid; replace it with the correct "bg-gradient-to-b" in
the div's className so the gradient renders properly (update the className
string to swap "bg-linear-to-b" -> "bg-gradient-to-b").

Comment on lines +44 to +48
<DownloadCard
iconName="simple-icons:apple"
spec="macOS 14.2+ (Apple Silicon)"
downloadUrl="#"
available={true}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Replace placeholder download URL with actual link.

The macOS (Apple Silicon) card is marked as available but uses a placeholder URL (#). Ensure this is updated with the actual download link before release.


🏁 Script executed:

#!/bin/bash
# Examine the download.tsx file to verify downloadUrl placeholder usage
cat apps/web/src/routes/_view/download.tsx

Length of output: 4331


🏁 Script executed:

#!/bin/bash
# Search for downloadUrl patterns and any constants/environment vars that might populate URLs
rg -nP 'downloadUrl|DOWNLOAD|download.*url' apps/web/src/routes/_view/download.tsx -A 2 -B 2

Length of output: 1630


🏁 Script executed:

#!/bin/bash
# Check for any constants files or config that might contain download URLs
fd -e ts -e tsx -e js -e json 'download|url|config' apps/web/src/ --type f | head -20

Length of output: 82


🏁 Script executed:

#!/bin/bash
# Check download-button.tsx for any URL patterns or constants
cat apps/web/src/components/download-button.tsx

Length of output: 926


Replace placeholder download URL with actual macOS download link.

The macOS Apple Silicon card (line 47) is the only available platform but has a non-functional placeholder URL (downloadUrl="#"). This must be updated with the actual download link before release.

🤖 Prompt for AI Agents
In apps/web/src/routes/_view/download.tsx around lines 44 to 48, the macOS Apple
Silicon DownloadCard uses a placeholder URL (downloadUrl="#"); replace that
placeholder with the actual macOS Apple Silicon download link (the signed
.dmg/.pkg or the public CDN/path) so the card is clickable and functional —
update the prop to the real URL, keep available={true}, and ensure the URL is an
absolute HTTPS link (or internal route) before merging.

? (
<a
href={downloadUrl}
className="group w-full px-4 h-11 flex items-center justify-center bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all text-base font-medium"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix invalid Tailwind gradient class.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

-            className="group w-full px-4 h-11 flex items-center justify-center bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all text-base font-medium"
+            className="group w-full px-4 h-11 flex items-center justify-center bg-gradient-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all text-base font-medium"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className="group w-full px-4 h-11 flex items-center justify-center bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all text-base font-medium"
className="group w-full px-4 h-11 flex items-center justify-center bg-gradient-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all text-base font-medium"
🤖 Prompt for AI Agents
In apps/web/src/routes/_view/download.tsx around line 116, the Tailwind class
`bg-linear-to-t` is invalid; replace it with the correct `bg-gradient-to-t` in
the className string so the gradient utility is applied correctly (update the
className value to use `bg-gradient-to-t` while leaving the rest of the classes
unchanged).

Comment on lines +197 to +204
<a
className="decoration-dotted underline hover:text-stone-600 transition-all"
href="https://github.com/fastrepl/hyprnote"
target="_blank"
>
open source
</a>
</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add missing rel attribute for security.

The external link uses target="_blank" without rel="noopener noreferrer", which is a security risk (tab-nabbing).

Apply this diff:

               <a
                 className="decoration-dotted underline hover:text-stone-600 transition-all"
                 href="https://github.com/fastrepl/hyprnote"
                 target="_blank"
+                rel="noopener noreferrer"
               >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a
className="decoration-dotted underline hover:text-stone-600 transition-all"
href="https://github.com/fastrepl/hyprnote"
target="_blank"
>
open source
</a>
</p>
<a
className="decoration-dotted underline hover:text-stone-600 transition-all"
href="https://github.com/fastrepl/hyprnote"
target="_blank"
rel="noopener noreferrer"
>
open source
</a>
</p>
🧰 Tools
🪛 Biome (2.1.2)

[error] 200-200: Avoid using target="_blank" without rel="noopener" or rel="noreferrer".

Opening external links in new tabs without rel="noopener" is a security risk. See the explanation for more details.
Safe fix: Add the rel="noopener" attribute.

(lint/security/noBlankTarget)

🤖 Prompt for AI Agents
In apps/web/src/routes/_view/index.tsx around lines 197 to 204, the anchor tag
opens an external link with target="_blank" but lacks rel="noopener noreferrer";
add rel="noopener noreferrer" to the <a> element to prevent tab-nabbing and
ensure the link is opened securely in a new tab.

function Component() {
return <div>Hello "/_view/_layout/pricing"!</div>;
return (
<main className="flex-1 bg-linear-to-b from-white via-stone-50/20 to-white min-h-screen">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix invalid Tailwind gradient class.

bg-linear-to-b is not a valid Tailwind class. Use bg-gradient-to-b instead.

Apply this diff:

-    <main className="flex-1 bg-linear-to-b from-white via-stone-50/20 to-white min-h-screen">
+    <main className="flex-1 bg-gradient-to-b from-white via-stone-50/20 to-white min-h-screen">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<main className="flex-1 bg-linear-to-b from-white via-stone-50/20 to-white min-h-screen">
<main className="flex-1 bg-gradient-to-b from-white via-stone-50/20 to-white min-h-screen">
🤖 Prompt for AI Agents
In apps/web/src/routes/_view/pricing.tsx around line 86, the Tailwind class
"bg-linear-to-b" is invalid; replace it with the correct "bg-gradient-to-b" to
enable the intended top-to-bottom gradient; update the className value to use
"bg-gradient-to-b" (keeping the other utility classes unchanged).

Comment on lines +213 to +214
? "bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%]"
: "bg-linear-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%]",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix invalid Tailwind gradient classes.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

             plan.popular
-              ? "bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%]"
-              : "bg-linear-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%]",
+              ? "bg-gradient-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%]"
+              : "bg-gradient-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%]",
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
? "bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%]"
: "bg-linear-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%]",
? "bg-gradient-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%]"
: "bg-gradient-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%]",
🤖 Prompt for AI Agents
In apps/web/src/routes/_view/pricing.tsx around lines 213 to 214, the Tailwind
class name "bg-linear-to-t" is invalid; replace both occurrences with the
correct "bg-gradient-to-t" so the gradient utilities apply properly while
keeping the rest of the class strings unchanged.


function CTASection() {
return (
<section className="py-16 border-t border-neutral-100 bg-linear-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix invalid Tailwind gradient class.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

-    <section className="py-16 border-t border-neutral-100 bg-linear-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
+    <section className="py-16 border-t border-neutral-100 bg-gradient-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<section className="py-16 border-t border-neutral-100 bg-linear-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
<section className="py-16 border-t border-neutral-100 bg-gradient-to-t from-stone-50/30 to-stone-100/30 px-4 laptop:px-0">
🤖 Prompt for AI Agents
In apps/web/src/routes/_view/pricing.tsx around line 296, the section element
uses an invalid Tailwind class `bg-linear-to-t`; replace it with the correct
`bg-gradient-to-t` class so the background gradient renders properly (i.e.,
update the className value to use `bg-gradient-to-t` instead of
`bg-linear-to-t`).

Comment on lines +316 to +324
className="px-8 h-10 flex items-center justify-center text-sm bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
>
Download for Free
</a>
<a
href="https://github.com/fastrepl/hyprnote"
target="_blank"
rel="noopener noreferrer"
className="px-8 h-10 flex items-center justify-center text-sm bg-linear-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%] transition-all"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix invalid Tailwind gradient classes in CTA buttons.

bg-linear-to-t is not a valid Tailwind class. Use bg-gradient-to-t instead.

Apply this diff:

             target="_blank"
             rel="noopener noreferrer"
-            className="px-8 h-10 flex items-center justify-center text-sm bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
+            className="px-8 h-10 flex items-center justify-center text-sm bg-gradient-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
           >
             Download for Free
           </a>
           <a
             href="https://github.com/fastrepl/hyprnote"
             target="_blank"
             rel="noopener noreferrer"
-            className="px-8 h-10 flex items-center justify-center text-sm bg-linear-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%] transition-all"
+            className="px-8 h-10 flex items-center justify-center text-sm bg-gradient-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%] transition-all"
           >
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
className="px-8 h-10 flex items-center justify-center text-sm bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
>
Download for Free
</a>
<a
href="https://github.com/fastrepl/hyprnote"
target="_blank"
rel="noopener noreferrer"
className="px-8 h-10 flex items-center justify-center text-sm bg-linear-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%] transition-all"
className="px-8 h-10 flex items-center justify-center text-sm bg-gradient-to-t from-stone-600 to-stone-500 text-white rounded-full shadow-md hover:shadow-lg hover:scale-[102%] active:scale-[98%] transition-all"
>
Download for Free
</a>
<a
href="https://github.com/fastrepl/hyprnote"
target="_blank"
rel="noopener noreferrer"
className="px-8 h-10 flex items-center justify-center text-sm bg-gradient-to-t from-neutral-200 to-neutral-100 text-neutral-900 rounded-full shadow-sm hover:shadow-md hover:scale-[102%] active:scale-[98%] transition-all"
🤖 Prompt for AI Agents
In apps/web/src/routes/_view/pricing.tsx around lines 316 to 324, the CTA
anchors use an invalid Tailwind class "bg-linear-to-t"; replace that with the
correct Tailwind gradient class "bg-gradient-to-t" for both buttons so the
gradient utilities apply properly, keeping the rest of the className strings
unchanged.

@yujonglee yujonglee merged commit 02ac75f into main Oct 29, 2025
3 of 4 checks passed
@yujonglee yujonglee deleted the jj-branch-29 branch October 29, 2025 00:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants