Skip to content

Conversation

@ComputelessComputer
Copy link
Collaborator

@ComputelessComputer ComputelessComputer commented Nov 28, 2025

Add SEO-ready pages, template pages with OG images, and shortcuts collection

Summary

This PR adds SEO-ready landing pages, a company handbook with docs-style layout, industry/role-specific solution pages, individual template detail pages with OG image support, and a new shortcuts content collection for AI assistant prompts.

self-hosting.tsx & local-ai.tsx:

  • Added Open Graph meta tags for social sharing
  • Expanded self-hosting from "Coming Soon" placeholder to comprehensive landing page with sections: Why self-host, Self-hosted vs Cloud comparison, Deployment options, What you can self-host, Built for enterprise, Open source foundation

New /free page:

  • Created comprehensive value proposition page similar to Otter AI's approach
  • Showcases 6 free features, comparison with alternatives, 6 use cases
  • Includes open source/transparency section and CTAs

New /company-handbook:

  • Docs-style layout with left sidebar navigation and table of contents
  • Added handbook collection to content-collections.ts
  • Created 18 MDX content files organized into 5 sections:
    • About (What Hyprnote is, How it was born, The team, Why important, Vision)
    • How we work (Work styles, Communication and focus, Customer feedback)
    • Who we want (Core traits, Hard skills, Founders example, Future teammates)
    • Go-to-market (Customers, Value, Discovery, Communication, Pricing)
    • Communication (Why this matters, North star, Principles)

New /solution/[slug] pages (9 pages):

  • Created SEO-ready landing pages for industries and roles:
    • Sales, Healthcare, Legal, Media, Customer Success, Field Engineering, Recruiting, Project Management, Government
  • Each page includes: SEO meta tags (title, description, og:*), hero section, 6 features with icons, 4 use cases, CTA section
  • Consistent design pattern across all pages with industry-specific content and messaging

New /templates/$slug pages (individual template detail pages):

  • Created individual template detail pages at /templates/{slug} with full OG image support
  • OG image URL pattern: https://hyprnote.com/og?type=templates&title=...&category=...&description=...
  • Each page includes: template icon, category badge, title, description, "Best for" targets, numbered template sections, MDX content preview, download CTA
  • Redirects to /templates if template slug not found

OG image endpoint update (apps/web/netlify/edge-functions/og.tsx):

  • Added templatesSchema for type=templates parameter validation
  • Added renderTemplatesTemplate() function with same styling as docs OG images
  • Updated handler to route type=templates requests to the new render function
  • Added custom fonts support for templates OG images

New shortcuts content collection:

  • Added shortcuts collection to content-collections.ts for AI assistant prompts
  • Schema includes: title, description, category, and prompt (the system prompt)
  • Created 6 example shortcuts:
    • Sales Call Feedback - analyze sales calls and get coaching feedback
    • Extract Action Items - pull out tasks and commitments from meetings
    • Draft Follow-up Email - generate professional follow-up emails
    • Meeting Insights - deep analysis of meeting dynamics and themes
    • Customer Concerns Analysis - identify and prioritize customer objections
    • Decision Summary - document all decisions made during meetings
  • Note: This sets up the content collection only; no UI/route for shortcuts yet

Updates since last revision

  • Fixed Twitter handle in company handbook footer from @hyprnote to @tryhyprnote (CodeRabbit review feedback)

Review & Testing Checklist for Human

  • Approve argos/web visual regression: CI shows visual changes waiting for your decision. Review and approve in Argos UI.
  • Verify shortcuts schema makes sense: The prompt field contains the system prompt for AI assistant. Confirm this structure works for the intended use case.
  • Verify handbook content accuracy: Content was extracted from the Google Doc. Confirm all sections are complete and accurately reflect the source document.
  • Verify self-hosting claims: The page mentions SSO/LDAP, HIPAA/SOC 2 readiness, Docker/Kubernetes deployment. Confirm these align with actual capabilities.
  • Test OG image generation for templates: Visit deploy preview and check /og?type=templates&title=Daily%20Standup&category=Engineering renders correctly.

Recommended test plan:

  1. Visit deploy preview: https://deploy-preview-1968--hyprnote.netlify.app
  2. Navigate to /templates/daily-standup - verify template detail page renders with icon, sections, and preview
  3. Navigate to /company-handbook - verify redirect to first page, test sidebar navigation
  4. Navigate to /free - scroll through all sections
  5. Navigate to each /solution/* page - verify content and icons
  6. Use browser dev tools to verify OG meta tags on template and solution pages

Notes

- Add comprehensive OG meta tags (og:title, og:description, og:type, og:url) to both pages
- Expand self-hosting.tsx from a simple 'Coming Soon' page to a full landing page with:
  - Why self-host section (data sovereignty, compliance, air-gapped, customization)
  - Self-hosted vs Cloud comparison
  - Deployment options (Docker, Kubernetes, bare metal, private cloud, air-gapped, on-premises)
  - What you can self-host (transcription server, LLM inference, data storage)
  - Enterprise features (HIPAA/SOC 2, SSO/LDAP, analytics, support)
  - Open source foundation section
  - CTA section with contact sales link
- Update local-ai.tsx meta description to include more SEO keywords
- Both pages now ready for landing visitors with specific keywords and backlinking from blogs

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@netlify
Copy link

netlify bot commented Nov 28, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit bcb593b
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/6929dbdb7c00960008161c09
😎 Deploy Preview https://deploy-preview-1968--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify
Copy link

netlify bot commented Nov 28, 2025

Deploy Preview for hyprnote-storybook ready!

Name Link
🔨 Latest commit bcb593b
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote-storybook/deploys/6929dbdb2f62e00008cfec76
😎 Deploy Preview https://deploy-preview-1968--hyprnote-storybook.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@argos-ci
Copy link

argos-ci bot commented Nov 28, 2025

The latest updates on your projects. Learn more about Argos notifications ↗︎

Awaiting the start of a new Argos build…

- Create /free page showcasing Hyprnote's value proposition for SEO
- Add /company-handbook route with sidebar navigation and content layout
- Create handbook content collection in content-collections.ts
- Add handbook content from company handbook document:
  - About section (What Hyprnote is, How it was born, Team, Why important, Vision)
  - How we work section (Work styles, Communication, Customer feedback)
  - Who we want section (Core traits, Hard skills, Founders example)
  - Go to market section (Customers, Value, Discovery, Communication, Pricing)
  - Communication section (Why it matters, North star, Principles)

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 28, 2025

📝 Walkthrough

Walkthrough

Adds handbook and shortcuts content collections and many MDX pages; implements a company-handbook route set with HandbookLayout and TOC/sidebar; updates numerous marketing/solution pages and templates metadata; extends the Netlify OG edge function to support a new templates type.

Changes

Cohort / File(s) Summary
Content collections
apps/web/content-collections.ts
Add handbook and shortcuts collections with schemas and async transforms that compile MDX (remarkGfm, mdxMermaid, rehypeSlug/autolink), compute toc/slug/sectionFolder/isIndex/order and include them in exported collections.
Handbook content
apps/web/content/handbook/.../*.mdx
Add ~15 MDX handbook pages across about, communication, go-to-market, how-we-work, and who-we-want with frontmatter and static content.
Shortcuts content
apps/web/content/shortcuts/*.mdx
Add shortcut MDX files (action-items.mdx, customer-concerns.mdx, decision-summary.mdx, follow-up-email.mdx, meeting-insights.mdx, sales-feedback.mdx) with prompts and guidance.
Handbook routes & config
apps/web/src/routes/_view/company-handbook/index.tsx, $.tsx, route.tsx, structure.ts
New handbook route set: index redirects to a default doc, dynamic splat route with beforeLoad/loader/head, a layout route with left navigation; handbookStructure defines sections and default pages.
Handbook UI components
apps/web/src/routes/_view/company-handbook/-components.tsx
Add HandbookLayout and subcomponents (ArticleHeader, ArticleContent, ArticleFooter, RightSidebar/TOC) that render MDX content, metadata, and a sticky TOC sidebar.
Templates detail route
apps/web/src/routes/_view/templates/$slug.tsx
Update templates/meeting detail route head/meta and OG data (title, og:type, og:image, twitter card); adjust title/OG semantics and image handling.
OG edge function updates
apps/web/netlify/edge-functions/og.tsx
Extend OGSchema to include templates type, rename template renderers (renderMeetingTemplate), add renderTemplatesTemplate, update parseSearchParams and handler routing, and include templates in font logic.
Marketing / product pages
apps/web/src/routes/_view/free.tsx, .../product/local-ai.tsx, .../product/self-hosting.tsx
Add new /free page; expand Local AI metadata; redesign Self-hosting into a multi-section enterprise page with updated OG/meta and links.
Solution pages (many)
apps/web/src/routes/_view/solution/*.tsx
Replace placeholder RouteComponent pages with full Component implementations and add head() metadata (SEO/OG) for customer-success, field-engineering, government, healthcare, legal, media, project-management, recruiting, sales.
Misc head/meta tweaks
apps/web/src/routes/_view/templates/$slug.tsx, .../product/local-ai.tsx, etc.
Adjust OG/meta titles, descriptions, and images for templates and several product/solution pages.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Router as React Router
    participant HandbookRoute as Handbook Route
    participant Collections as Content Collections
    participant MDX as MDX Compiler
    participant Layout as HandbookLayout

    User->>Router: Navigate to /company-handbook/<splat>
    Router->>HandbookRoute: match dynamic handbook route
    HandbookRoute->>HandbookRoute: beforeLoad (validate/redirect)
    HandbookRoute->>Collections: loader -> resolve doc by slug/_splat
    Collections-->>HandbookRoute: return doc { mdx, toc, slug, order, meta }
    HandbookRoute->>Layout: render HandbookLayout with doc
    Layout->>MDX: render compiled MDX to React
    MDX-->>Layout: rendered content
    Layout-->>User: display article, sidebar TOC, navigation
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • apps/web/content-collections.ts — MDX transform pipeline, slug/ordering, include/exclude patterns.
    • apps/web/src/routes/_view/company-handbook/$.tsx and index.tsx — beforeLoad/redirect and splat resolution.
    • apps/web/src/routes/_view/company-handbook/-components.tsx — MDX rendering, TOC extraction, responsive sidebar behavior.
    • apps/web/netlify/edge-functions/og.tsx — new templates type handling, parser/handler updates, renamed render functions.
    • Bulk updates to many solution/product routes — verify head metadata, routing links, and consistent UI imports.

Possibly related PRs

Suggested reviewers

  • yujonglee

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
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.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding SEO-ready pages, template pages with OG images, and a shortcuts collection.
Description check ✅ Passed The description is comprehensive and clearly related to the changeset, detailing all major additions including handbook, solution pages, templates, OG images, and shortcuts collection.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/1764315131-seo-landing-pages

📜 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 85a958b and bcb593b.

📒 Files selected for processing (1)
  • apps/web/src/routes/_view/templates/$slug.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/routes/_view/templates/$slug.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: ci (macos, macos-14)

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

@devin-ai-integration devin-ai-integration bot changed the title Make self-hosting and local-ai pages SEO-ready Add SEO-ready pages: self-hosting, local-ai, /free, and /company-handbook Nov 28, 2025
Copy link
Contributor

@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

🧹 Nitpick comments (10)
apps/web/src/routes/_view/free.tsx (1)

233-285: Consider using semantic table elements for accessibility.

The comparison grid uses div elements with grid-cols-3. For better screen reader support, consider using semantic <table>, <thead>, <tbody>, <tr>, <th>, and <td> elements. This helps assistive technologies understand the data relationships.

apps/web/src/routes/_view/product/local-ai.tsx (1)

16-26: Consider adding og:image for enhanced social sharing.

The Open Graph meta tags are well-implemented with appropriate title, description, type, and URL. However, an og:image tag would significantly improve how this page appears when shared on social media platforms (Twitter, LinkedIn, Slack, etc.).

Add an og:image meta tag:

       { property: "og:type", content: "website" },
       {
         property: "og:url",
         content: "https://hyprnote.com/product/local-ai",
       },
+      {
+        property: "og:image",
+        content: "https://hyprnote.com/images/og/local-ai.png",
+      },
     ],
apps/web/content/handbook/who-we-want/2.founders-example.mdx (1)

7-7: Consider varying sentence structure for improved readability.

Three consecutive sentences begin with "We," which static analysis flagged. While this repetition might be intentional for emphasis, varying the structure could enhance readability.

Example alternative:

-We hold ourselves to the same standards we expect from others. We're not perfect, but we try to embody grit, honesty, and sharpness every day. We expect future teammates to do the same — and to call us out when we fall short.
+We hold ourselves to the same standards we expect from others. Though not perfect, we try to embody grit, honesty, and sharpness every day. Future teammates are expected to do the same — and to call us out when we fall short.
apps/web/content/handbook/how-we-work/1.communication-and-focus.mdx (1)

7-15: Minor wording tweak for the “open documentation culture” bullet

The last bullet reads a bit awkwardly; adding “that” smooths it out:

-**Open documentation culture** we're still building, so everyone can contribute and learn.
+**Open documentation culture** that we're still building, so everyone can contribute and learn.
apps/web/src/routes/_view/company-handbook/$.tsx (1)

7-45: Make loader robust to missing docs and avoid doc! non‑null assertion

The route logic overall looks good (normalization, section default redirects, and SEO meta), but the loader currently assumes doc is always found:

return { doc: doc! };

If allHandbooks or slugs ever drift from what beforeLoad expects, this becomes a runtime error instead of a controlled redirect/404. You can tighten this up and de‑duplicate the lookup logic a bit:

-  loader: async ({ params }) => {
-    const splat = params._splat || "";
-    const normalizedSplat = splat.replace(/\/$/, "");
-
-    let doc = allHandbooks.find((doc) => doc.slug === normalizedSplat);
-    if (!doc) {
-      doc = allHandbooks.find((doc) => doc.slug === `${normalizedSplat}/index`);
-    }
-
-    return { doc: doc! };
-  },
+  loader: async ({ params }) => {
+    const splat = params._splat || "";
+    const normalizedSplat = splat.replace(/\/$/, "");
+
+    const doc =
+      allHandbooks.find((doc) => doc.slug === normalizedSplat) ??
+      allHandbooks.find((doc) => doc.slug === `${normalizedSplat}/index`);
+
+    if (!doc) {
+      // Fallback to the canonical intro page instead of throwing at runtime.
+      throw redirect({
+        to: "/company-handbook/$",
+        params: { _splat: "about/what-hyprnote-is" },
+      });
+    }
+
+    return { doc };
+  },

Optionally, you could also extract the slug‑normalization and doc‑lookup into a small helper so beforeLoad and loader share the same behavior.

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

3-9: Consider importing default page from structure.ts.

The default splat "about/what-hyprnote-is" is duplicated here and in structure.ts. For single source of truth:

 import { createFileRoute, redirect } from "@tanstack/react-router";
+import { handbookStructure } from "./structure";

 export const Route = createFileRoute("/_view/company-handbook/")({
   beforeLoad: () => {
     throw redirect({
       to: "/company-handbook/$",
-      params: { _splat: "about/what-hyprnote-is" },
+      params: { _splat: handbookStructure.defaultPages.about },
     });
   },
 });
apps/web/src/routes/_view/product/self-hosting.tsx (1)

481-490: Use internal Link component for /founders route.

The "Contact sales" button uses an absolute URL with <a> tag, but /founders appears to be an internal route. Using Link enables client-side navigation and prefetching.

-            <a
-              href="https://hyprnote.com/founders"
+            <Link
+              to="/founders"
               className={cn([
                 "px-8 py-3 text-base font-medium rounded-full",
                 "bg-linear-to-t from-stone-600 to-stone-500 text-white",
                 "hover:scale-105 active:scale-95 transition-transform",
               ])}
             >
               Contact sales
-            </a>
+            </Link>
apps/web/src/routes/_view/company-handbook/route.tsx (1)

86-98: Use cn utility for conditional classNames.

Per coding guidelines, when there are classNames with conditional logic, use cn from @hypr/utils.

+import { cn } from "@hypr/utils";
+
 // In the Link component:
-                  className={`block px-3 py-1.5 text-sm rounded-sm transition-colors ${
-                    currentSlug === doc.slug
-                      ? "bg-neutral-100 text-stone-600 font-medium"
-                      : "text-neutral-600 hover:text-stone-600 hover:bg-neutral-50"
-                  }`}
+                  className={cn([
+                    "block px-3 py-1.5 text-sm rounded-sm transition-colors",
+                    currentSlug === doc.slug
+                      ? "bg-neutral-100 text-stone-600 font-medium"
+                      : "text-neutral-600 hover:text-stone-600 hover:bg-neutral-50",
+                  ])}

Based on learnings, cn should be used with an array split by logical grouping.

apps/web/src/routes/_view/company-handbook/-components.tsx (1)

221-233: Use Link component for internal /founders route.

The "Talk to us" CTA uses <a href="/founders"> with target="_blank", but /founders is an internal route. This should use the Link component for client-side navigation and shouldn't open in a new tab.

-        <a
-          href="/founders"
-          target="_blank"
-          rel="noopener noreferrer"
+        <Link
+          to="/founders"
           className={cn([
             "group px-4 h-9 flex items-center justify-center text-sm w-full",
             "bg-linear-to-t from-stone-600 to-stone-500 text-white rounded-full",
             "hover:scale-[102%] active:scale-[98%]",
             "transition-all",
           ])}
         >
           Talk to us
-        </a>
+        </Link>
apps/web/content-collections.ts (1)

404-463: Handbook collection implementation looks solid; duplication with docs transform could be abstracted

The handbook collection schema and transform are consistent with existing patterns:

  • MDX compilation and rehype/remark plugins match other collections.
  • toc, slug, sectionFolder, isIndex, and order are derived identically to docs, which should integrate cleanly with handbook routes.
  • Adding handbook to the collections array is correct and keeps config cohesive.

Given docs and handbook now share nearly identical transform logic, you might later extract a small shared helper (e.g., buildMdxDocWithToc(document, context)) to reduce duplication and keep future changes (like slug rules or heading levels) in one place, but this is optional and not blocking.

Also applies to: 475-475

📜 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 35b1859 and feca068.

⛔ Files ignored due to path filters (1)
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
📒 Files selected for processing (29)
  • apps/web/content-collections.ts (2 hunks)
  • apps/web/content/handbook/about/0.what-hyprnote-is.mdx (1 hunks)
  • apps/web/content/handbook/about/1.how-hyprnote-was-born.mdx (1 hunks)
  • apps/web/content/handbook/about/2.the-team.mdx (1 hunks)
  • apps/web/content/handbook/about/3.why-hyprnote-is-important.mdx (1 hunks)
  • apps/web/content/handbook/about/4.where-were-heading.mdx (1 hunks)
  • apps/web/content/handbook/communication/0.why-this-matters.mdx (1 hunks)
  • apps/web/content/handbook/communication/1.our-north-star.mdx (1 hunks)
  • apps/web/content/handbook/communication/2.principles.mdx (1 hunks)
  • apps/web/content/handbook/go-to-market/0.customers.mdx (1 hunks)
  • apps/web/content/handbook/go-to-market/1.value-we-provide.mdx (1 hunks)
  • apps/web/content/handbook/go-to-market/2.how-customers-find-us.mdx (1 hunks)
  • apps/web/content/handbook/go-to-market/3.how-we-talk-with-them.mdx (1 hunks)
  • apps/web/content/handbook/go-to-market/4.how-we-price.mdx (1 hunks)
  • apps/web/content/handbook/how-we-work/0.work-styles.mdx (1 hunks)
  • apps/web/content/handbook/how-we-work/1.communication-and-focus.mdx (1 hunks)
  • apps/web/content/handbook/how-we-work/2.customer-feedback.mdx (1 hunks)
  • apps/web/content/handbook/who-we-want/0.core-traits.mdx (1 hunks)
  • apps/web/content/handbook/who-we-want/1.hard-skills.mdx (1 hunks)
  • apps/web/content/handbook/who-we-want/2.founders-example.mdx (1 hunks)
  • apps/web/content/handbook/who-we-want/3.future-teammates.mdx (1 hunks)
  • apps/web/src/routes/_view/company-handbook/$.tsx (1 hunks)
  • apps/web/src/routes/_view/company-handbook/-components.tsx (1 hunks)
  • apps/web/src/routes/_view/company-handbook/index.tsx (1 hunks)
  • apps/web/src/routes/_view/company-handbook/route.tsx (1 hunks)
  • apps/web/src/routes/_view/company-handbook/structure.ts (1 hunks)
  • apps/web/src/routes/_view/free.tsx (1 hunks)
  • apps/web/src/routes/_view/product/local-ai.tsx (1 hunks)
  • apps/web/src/routes/_view/product/self-hosting.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/src/routes/_view/company-handbook/structure.ts
  • apps/web/content-collections.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/company-handbook/structure.ts
  • apps/web/src/routes/_view/product/local-ai.tsx
  • apps/web/src/routes/_view/company-handbook/-components.tsx
  • apps/web/src/routes/_view/company-handbook/$.tsx
  • apps/web/src/routes/_view/company-handbook/index.tsx
  • apps/web/src/routes/_view/product/self-hosting.tsx
  • apps/web/src/routes/_view/company-handbook/route.tsx
  • apps/web/content-collections.ts
  • apps/web/src/routes/_view/free.tsx
🧠 Learnings (2)
📚 Learning: 2025-11-24T16:32:19.706Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:19.706Z
Learning: Applies to **/*.{ts,tsx} : If there are many classNames with conditional logic, use `cn` (import from `hypr/utils`), similar to `clsx`. Always pass an array and split by logical grouping.

Applied to files:

  • apps/web/src/routes/_view/product/self-hosting.tsx
📚 Learning: 2025-11-24T16:32:23.055Z
Learnt from: CR
Repo: fastrepl/hyprnote PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T16:32:23.055Z
Learning: Applies to **/*.{ts,tsx} : If there are many classNames with conditional logic, use `cn` (import from `hypr/utils`). It is similar to `clsx`. Always pass an array and split by logical grouping.

Applied to files:

  • apps/web/src/routes/_view/product/self-hosting.tsx
🧬 Code graph analysis (5)
apps/web/src/routes/_view/company-handbook/$.tsx (5)
apps/web/src/routes/_view/company-handbook/index.tsx (1)
  • Route (3-10)
apps/web/src/routes/_view/company-handbook/route.tsx (1)
  • Route (12-14)
apps/web/src/routes/_view/free.tsx (1)
  • Route (6-26)
apps/web/src/routes/_view/company-handbook/structure.ts (1)
  • handbookStructure (1-10)
apps/web/src/routes/_view/company-handbook/-components.tsx (1)
  • HandbookLayout (17-35)
apps/web/src/routes/_view/company-handbook/index.tsx (2)
apps/web/src/routes/_view/company-handbook/$.tsx (1)
  • Route (7-61)
apps/web/src/routes/_view/company-handbook/route.tsx (1)
  • Route (12-14)
apps/web/src/routes/_view/product/self-hosting.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/company-handbook/route.tsx (3)
apps/web/src/routes/_view/company-handbook/$.tsx (1)
  • Route (7-61)
apps/web/src/routes/_view/company-handbook/index.tsx (1)
  • Route (3-10)
apps/web/src/routes/_view/company-handbook/structure.ts (1)
  • handbookStructure (1-10)
apps/web/src/routes/_view/free.tsx (2)
apps/web/src/routes/_view/company-handbook/route.tsx (1)
  • Route (12-14)
packages/utils/src/cn.ts (1)
  • cn (20-22)
🪛 LanguageTool
apps/web/content/handbook/who-we-want/2.founders-example.mdx

[style] ~7-~7: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...grit, honesty, and sharpness every day. We expect future teammates to do the same ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

apps/web/content/handbook/go-to-market/3.how-we-talk-with-them.mdx

[style] ~15-~15: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...ving problems, not selling features. Be responsive — Treat every piece of fee...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~17-~17: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...every piece of feedback as valuable. Be transparent — Share our roadmap, ackn...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

apps/web/content/handbook/go-to-market/2.how-customers-find-us.mdx

[uncategorized] ~11-~11: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...k for itself. ## Discovery Channels Open source community — Our GitHub presence and c...

(EN_COMPOUND_ADJECTIVE_INTERNAL)

apps/web/content/handbook/about/4.where-were-heading.mdx

[grammar] ~6-~6: Ensure spelling is correct
Context: ...t tomorrow, it will be much more." --- Hyprnote today is a notetaker. But tomorrow, it ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

apps/web/content/handbook/about/0.what-hyprnote-is.mdx

[grammar] ~6-~6: Ensure spelling is correct
Context: ...cy-first, local-first philosophy." --- Hyprnote is the first open-source AI notetaker f...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)


[grammar] ~14-~14: Ensure spelling is correct
Context: ...your behalf. ## Simple and Accessible Hyprnote is simple, accessible, and platform-agn...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

apps/web/content/handbook/about/1.how-hyprnote-was-born.mdx

[grammar] ~6-~6: Ensure spelling is correct
Context: ...tion with existing meeting tools." --- Hyprnote was born from real frustration. ## The...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Redirect rules - hyprnote-storybook
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: Header rules - hyprnote-storybook
  • GitHub Check: Pages changed - hyprnote
🔇 Additional comments (31)
apps/web/content/handbook/who-we-want/1.hard-skills.mdx (1)

1-7: LGTM!

Frontmatter is well-structured with required fields, and the content clearly conveys the message about hard skills expectations.

apps/web/content/handbook/go-to-market/2.how-customers-find-us.mdx (1)

1-17: LGTM!

Content is well-structured with clear discovery channel descriptions. The static analysis hint about hyphenating "Open source" can be ignored here since it functions as a noun phrase naming the community type rather than a compound adjective.

apps/web/content/handbook/go-to-market/0.customers.mdx (1)

1-15: LGTM!

Clear customer segmentation that aligns well with Hyprnote's product positioning around privacy and data control.

apps/web/content/handbook/go-to-market/1.value-we-provide.mdx (1)

1-17: LGTM!

Value propositions are clearly articulated and consistent with Hyprnote's product positioning.

apps/web/content/handbook/communication/0.why-this-matters.mdx (1)

1-11: LGTM!

Good introductory content that sets the tone for the communication section.

apps/web/content/handbook/about/1.how-hyprnote-was-born.mdx (1)

1-19: LGTM!

Compelling origin story with clear founder perspectives. The static analysis hint about "Hyprnote" is a false positive — it's the product name.

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

6-26: LGTM! Well-structured SEO meta tags.

The head() function properly includes title, description, and Open Graph meta tags for social sharing. The OG URL correctly points to the canonical path.


112-116: Verify the "HIPAA-ready" claim accuracy.

The healthcare use case claims "HIPAA-ready with local processing." HIPAA compliance involves more than local processing (e.g., BAAs, audit trails, encryption requirements). Ensure this claim is accurate or consider softening to "designed with privacy in mind for healthcare use cases" to avoid potential compliance misrepresentation.


137-183: LGTM!

Clean hero section with proper responsive design and good use of the cn utility following the coding guidelines pattern.


185-219: LGTM!

Features section is well-implemented with clean mapping over the data array.


327-399: LGTM!

External GitHub link properly includes rel="noopener noreferrer" for security. Good balance of content and statistics.


119-135: LGTM!

Clean component composition with logical section ordering.

apps/web/content/handbook/who-we-want/0.core-traits.mdx (1)

1-17: Well-structured handbook content.

The content is clear, concise, and well-organized with proper frontmatter and logical sections. The three core traits are effectively communicated with practical context.

apps/web/src/routes/_view/product/local-ai.tsx (2)

14-26: Inconsistent AI summary detected.

The AI-generated summary states this PR "introduces a company handbook feature," but the PR objectives clearly describe SEO updates for self-hosting and local-ai product pages. This file's changes (adding OG meta tags) align with the PR objectives, not the summary.


14-26: Good SEO metadata implementation.

The enhanced meta description with specific keywords (Whisper, LLM models, meeting transcription) and the addition of Open Graph tags will improve search visibility and social sharing. The variation between the main description and OG description is intentional and appropriate for different contexts.

apps/web/content/handbook/communication/1.our-north-star.mdx (1)

1-7: Clear and actionable communication principle.

The North Star principle is well-defined and provides concrete criteria (educate, inspire, or solve a problem) for content decisions.

apps/web/content/handbook/about/2.the-team.mdx (1)

1-17: Well-crafted team introduction.

The founder profiles effectively highlight relevant expertise and technical contributions, while the shared values section provides cohesive context. The structure is clean and professional.

apps/web/content/handbook/about/3.why-hyprnote-is-important.mdx (1)

1-19: Strong positioning and value proposition.

The content effectively communicates Hyprnote's differentiators and market positioning. The structure progresses logically from problem statement to differentiators to core belief.

apps/web/content/handbook/go-to-market/3.how-we-talk-with-them.mdx (1)

1-17: Clear communication principles with effective parallel structure.

The content is well-organized with an honest introduction and four concise principles. The repetitive "Be [adjective]" structure (flagged by static analysis) is intentional parallelism that enhances clarity and memorability for a principles list.

apps/web/content/handbook/how-we-work/0.work-styles.mdx (1)

1-19: Thoughtful work culture documentation.

The content effectively balances individual work style descriptions with an overarching cultural philosophy. It humanizes the founders while establishing expectations around workplace diversity and autonomy.

apps/web/content/handbook/who-we-want/3.future-teammates.mdx (1)

1-7: Future teammates copy reads well and is consistent

Frontmatter and body text are clear, aligned with the “who-we-want” section, and don’t introduce any structural issues.

apps/web/content/handbook/go-to-market/4.how-we-price.mdx (1)

1-15: Confirm pricing/philosophy claims match the actual offering

The copy makes strong commitments (“core product is free and open source”, “Hyprnote experience is free forever”). Please double-check with current pricing plans and roadmap owners that these statements are accurate and durable enough to be in the handbook; otherwise consider softening (e.g., “designed to be…”, “we aim to keep…”) or being more specific about what’s free.

apps/web/content/handbook/how-we-work/2.customer-feedback.mdx (1)

1-11: Customer feedback process description looks solid

Clear explanation of consolidating feedback into GitHub Issues as the single public source of truth; wording and structure look good.

apps/web/content/handbook/about/4.where-were-heading.mdx (1)

1-19: Vision/ambition section is well-framed and consistent

Aspirational language is clearly scoped as “Our Vision / Our Ambition”, and the copy reads cleanly; no changes needed.

apps/web/content/handbook/communication/2.principles.mdx (1)

1-21: Communication principles page is clear and structured

The four principles are well-organized, easy to scan, and consistent with the rest of the handbook content.

apps/web/content/handbook/about/0.what-hyprnote-is.mdx (1)

1-15: Double-check the “first open‑source AI notetaker for the enterprise” claim

The copy positions Hyprnote as “the first open-source AI notetaker for the enterprise”. That’s a strong comparative claim; please confirm with product/marketing that this is accurate and that they’re comfortable with this positioning. If there’s any doubt or risk of future contradictions, consider softening to something like “one of the first” or focusing on the privacy/local‑first angle instead.

apps/web/src/routes/_view/company-handbook/structure.ts (1)

1-10: LGTM - Clean configuration structure.

The handbook structure is well-organized. Note that consumers (like LeftSidebar in route.tsx) transform section IDs to titles using only .charAt(0).toUpperCase() + sectionId.slice(1), which converts "how-we-work" to "How-we-work" rather than "How we work". This may be intentional if the content collection uses matching casing, but verify the sidebar displays section titles as expected.

apps/web/src/routes/_view/product/self-hosting.tsx (2)

8-28: LGTM - Well-structured OG meta tags for SEO.

The Open Graph metadata is properly configured with title, description, type, and canonical URL. This will improve social sharing previews.


76-83: Verify enterprise feature claims before launch.

The page makes specific claims about compliance and enterprise features:

  • "HIPAA, GDPR, SOC 2" readiness (Lines 80-81)
  • "SSO & LDAP integration" with "SAML, OIDC, and Active Directory out of the box" (Lines 396-400)

Per the PR checklist, these should be verified for accuracy. If these features are roadmap items rather than currently available, consider adjusting the copy (e.g., "designed for" or "supports").

Also applies to: 379-401

apps/web/src/routes/_view/company-handbook/-components.tsx (1)

17-23: LGTM - Component structure is well-organized.

The HandbookLayout cleanly composes header, content, footer, and sidebar. Using doc: any is acceptable here to avoid creating unnecessary interfaces per coding guidelines.

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

64-70: The review comment is incorrect — the section title transformation works correctly.

The transformation sectionId.charAt(0).toUpperCase() + sectionId.slice(1) converts:

  • "about""About" (matches section: "About" in content) ✓
  • "how-we-work""How-we-work" (matches section: "How-we-work" in content) ✓
  • "who-we-want""Who-we-want" (matches section: "Who-we-want" in content) ✓
  • "go-to-market""Go-to-market" (matches section: "Go-to-market" in content) ✓
  • "communication""Communication" (matches section: "Communication" in content) ✓

The handbook content files use hyphenated section names that align perfectly with the transformation output. There is no lookup failure occurring.

Likely an incorrect or invalid review comment.

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
@devin-ai-integration devin-ai-integration bot changed the title Add SEO-ready pages: self-hosting, local-ai, /free, and /company-handbook Add SEO-ready pages: self-hosting, local-ai, /free, /company-handbook, and /solution pages Nov 28, 2025
Copy link
Contributor

@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

🧹 Nitpick comments (2)
apps/web/src/routes/_view/company-handbook/structure.ts (1)

1-16: Structure looks good; consider narrowing the defaultPages typing

The mapping is coherent and all sections entries are represented in defaultPages, so this is functionally solid.

To make future edits safer, you could avoid the broad Record<string, string> assertion and tie the keys directly to the sections array. For example:

-export const handbookStructure = {
-  sections: [
-    "about",
-    "how-we-work",
-    "who-we-want",
-    "go-to-market",
-    "communication",
-  ],
-  defaultPages: {
-    about: "about/what-hyprnote-is",
-    "how-we-work": "how-we-work/work-styles",
-    "who-we-want": "who-we-want/core-traits",
-    "go-to-market": "go-to-market/customers",
-    communication: "communication/why-this-matters",
-  } as Record<string, string>,
-};
+const handbookSections = [
+  "about",
+  "how-we-work",
+  "who-we-want",
+  "go-to-market",
+  "communication",
+] as const;
+
+export const handbookStructure = {
+  sections: handbookSections,
+  defaultPages: {
+    about: "about/what-hyprnote-is",
+    "how-we-work": "how-we-work/work-styles",
+    "who-we-want": "who-we-want/core-traits",
+    "go-to-market": "go-to-market/customers",
+    communication: "communication/why-this-matters",
+  } satisfies Record<(typeof handbookSections)[number], string>,
+};

This keeps runtime behavior identical but ensures TypeScript enforces that every section has a default page and blocks extra keys.

apps/web/src/routes/_view/solution/legal.tsx (1)

93-241: Consider extracting shared section components (optional refactor).

All 7 solution pages in this PR share nearly identical HeroSection, FeaturesSection, UseCasesSection, and CTASection components — differing only in data/content. Consider extracting these into a shared SolutionPageLayout component that accepts the page-specific data as props:

// Example: shared solution page layout
function SolutionPageLayout({ 
  badge, 
  title, 
  description, 
  features, 
  useCases, 
  secondaryCTA 
}: SolutionPageProps) {
  return (
    <div className="...">
      <HeroSection badge={badge} title={title} ... />
      <FeaturesSection features={features} />
      <UseCasesSection useCases={useCases} />
      <CTASection />
    </div>
  );
}

This would reduce ~150 lines of duplicate code per file and make future styling updates easier. However, this can be deferred if pages are expected to diverge significantly.

📜 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 feca068 and 2f23597.

📒 Files selected for processing (10)
  • apps/web/src/routes/_view/company-handbook/structure.ts (1 hunks)
  • apps/web/src/routes/_view/solution/customer-success.tsx (1 hunks)
  • apps/web/src/routes/_view/solution/field-engineering.tsx (1 hunks)
  • apps/web/src/routes/_view/solution/government.tsx (1 hunks)
  • apps/web/src/routes/_view/solution/healthcare.tsx (1 hunks)
  • apps/web/src/routes/_view/solution/legal.tsx (1 hunks)
  • apps/web/src/routes/_view/solution/media.tsx (1 hunks)
  • apps/web/src/routes/_view/solution/project-management.tsx (1 hunks)
  • apps/web/src/routes/_view/solution/recruiting.tsx (1 hunks)
  • apps/web/src/routes/_view/solution/sales.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/solution/field-engineering.tsx
  • apps/web/src/routes/_view/company-handbook/structure.ts
  • apps/web/src/routes/_view/solution/customer-success.tsx
  • apps/web/src/routes/_view/solution/sales.tsx
  • apps/web/src/routes/_view/solution/legal.tsx
  • apps/web/src/routes/_view/solution/recruiting.tsx
  • apps/web/src/routes/_view/solution/media.tsx
  • apps/web/src/routes/_view/solution/project-management.tsx
  • apps/web/src/routes/_view/solution/government.tsx
  • apps/web/src/routes/_view/solution/healthcare.tsx
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/src/routes/_view/company-handbook/structure.ts
🧬 Code graph analysis (6)
apps/web/src/routes/_view/solution/sales.tsx (3)
apps/web/src/routes/_view/product/opensource.tsx (1)
  • Route (3-5)
apps/web/src/routes/_view/security.tsx (1)
  • Route (3-5)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/solution/legal.tsx (2)
apps/web/src/routes/_view/solution/sales.tsx (1)
  • Route (6-29)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/solution/recruiting.tsx (7)
apps/web/src/routes/_view/solution/customer-success.tsx (1)
  • Route (6-32)
apps/web/src/routes/_view/solution/government.tsx (1)
  • Route (6-32)
apps/web/src/routes/_view/solution/legal.tsx (1)
  • Route (6-29)
apps/web/src/routes/_view/solution/media.tsx (1)
  • Route (6-29)
apps/web/src/routes/_view/solution/project-management.tsx (1)
  • Route (6-32)
apps/web/src/routes/_view/solution/sales.tsx (1)
  • Route (6-29)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/solution/project-management.tsx (4)
apps/web/src/routes/_view/solution/sales.tsx (1)
  • Route (6-29)
apps/web/src/routes/_view/product/opensource.tsx (1)
  • Route (3-5)
apps/web/src/routes/_view/security.tsx (1)
  • Route (3-5)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/solution/government.tsx (4)
apps/web/src/routes/_view/solution/sales.tsx (1)
  • Route (6-29)
apps/web/src/routes/_view/product/opensource.tsx (1)
  • Route (3-5)
apps/web/src/routes/_view/security.tsx (1)
  • Route (3-5)
packages/utils/src/cn.ts (1)
  • cn (20-22)
apps/web/src/routes/_view/solution/healthcare.tsx (3)
apps/web/src/routes/_view/solution/legal.tsx (1)
  • Route (6-29)
apps/web/src/routes/_view/solution/sales.tsx (1)
  • Route (6-29)
packages/utils/src/cn.ts (1)
  • cn (20-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: fmt
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (21)
apps/web/src/routes/_view/solution/recruiting.tsx (4)

1-32: LGTM — Route and SEO metadata follow the established pattern.

The route definition with head() metadata is consistent with other solution pages in the codebase. SEO tags (title, description, og:*) are properly configured.


34-94: Data arrays are well-structured.

The features and useCases arrays provide clean, data-driven content for the page sections. Good use of consistent object shapes.


112-242: Section components are clean and follow established conventions.

The cn utility is used correctly with arrays as per coding guidelines. The responsive design with sm:, md:, lg: breakpoints is appropriate.


96-110: <call_tool>

#!/bin/bash

Check the ACTUAL installed Tailwind version by looking at node_modules

echo "=== Checking actual Tailwind version installed ==="
cat apps/web/node_modules/tailwindcss/package.json 2>/dev/null | grep '"version"' || echo "Checking root node_modules..."
cat node_modules/tailwindcss/package.json 2>/dev/null | grep '"version"' || echo "Cannot determine installed version"

Also check the CSS syntax - the @import and @theme are Tailwind v4 features

echo -e "\n=== Checking if bg-linear might be defined in @theme or elsewhere ==="
grep -r "linear" apps/web/src/index.css apps/web/src/globals.css 2>/dev/null

Search for any PostCSS plugins that might add bg-linear

echo -e "\n=== Looking for tailwind-scrollbar-hide version ==="
cat apps/web/node_modules/tailwind-scrollbar-hide/package.json 2>/dev/null | grep -E '"version"|"name"' || echo "Checking root..."
cat node_modules/tailwind-scrollbar-hide/package.json 2>/dev/null | grep -E '"version"|"name"'

Search if there are any Tailwind v4 features being used

echo -e "\n=== Checking if @theme syntax is v3 or v4 ==="
echo "The syntax used in index.css:"
echo "- @import 'tailwindcss' (v4 syntax)"
echo "- @theme { --breakpoint-laptop: 72rem; } (v4 syntax)"
echo "- @plugin '@tailwindcss/typography' (v4 syntax)"
echo ""
echo "This indicates Tailwind v4, not v3"


</function_calls>

<function_calls>

#!/bin/bash

The CSS file uses @import and @theme which are v4 syntax

Let's verify by checking if these are valid in v3

Also search for where bg-linear-* gradient utilities might come from

echo "=== Checking what defines bg-linear utilities ==="

Search in all node_modules for tailwind-related packages

find . -path "/node_modules/tailwindcss/" -name ".js" -o -name ".json" 2>/dev/null | grep -i "gradient|linear" | head -5

Check if tailwind-scrollbar-hide or other plugins define bg-linear

echo -e "\n=== Checking tailwind-scrollbar-hide plugin ==="
find . -path "/node_modules/tailwind-scrollbar-hide/" -type f 2>/dev/null | head -10

Most direct approach: search npm/web for bg-linear documentation

echo -e "\n=== Searching codebase for any documentation or comments about bg-linear ==="
rg "bg-linear" --type ts --type tsx --type js --type md -B5 2>/dev/null | head -50


</function_calls>

Let me take a web search approach to verify if bg-linear-to-b is valid in Tailwind v3 or v4:
<function_calls>

Tailwind CSS bg-linear-to-b gradient class valid syntax

</function_calls>

apps/web/src/routes/_view/solution/field-engineering.tsx (1)

1-242: LGTM — Consistent implementation for field engineering solution page.

The page follows the established pattern with appropriate content tailored for field engineers. The "Offline Capable" feature and link to /product/local-ai are contextually relevant for the target audience.

apps/web/src/routes/_view/solution/media.tsx (1)

1-240: LGTM — Media & Entertainment solution page is well-structured.

The content is appropriately tailored for creative teams with relevant features like "Capture Creative Ideas" and "Protect Creative IP". SEO metadata is properly configured.

apps/web/src/routes/_view/solution/government.tsx (2)

42-46: Verify compliance claims per PR checklist.

The feature mentions "FedRAMP, FISMA, and other government compliance requirements." As noted in the PR testing checklist, ensure these capability claims are accurate before publishing.


1-41: LGTM — Government solution page appropriately emphasizes security.

The focus on local processing, self-hosting, and compliance is well-suited for government agency audiences. The secondary CTA linking to /product/self-hosting is contextually appropriate.

Also applies to: 47-244

apps/web/src/routes/_view/solution/sales.tsx (1)

1-242: LGTM — Sales solution page is well-crafted.

The content is appropriately tailored for sales teams with relevant features like "Deal Intelligence" and "Sales Coaching Insights". SEO metadata is properly configured.

apps/web/src/routes/_view/solution/customer-success.tsx (1)

1-243: LGTM — Customer Success solution page is well-designed.

The content effectively targets CS teams with relevant features like "Track Health Signals" and "Renewal Preparation". SEO metadata follows the established pattern.

apps/web/src/routes/_view/solution/legal.tsx (1)

1-241: LGTM — Legal solution page appropriately emphasizes confidentiality.

The focus on attorney-client privilege, local AI processing, and self-hosting is well-suited for legal professionals. The secondary CTA linking to /product/local-ai reinforces the privacy-first messaging.

apps/web/src/routes/_view/solution/project-management.tsx (5)

1-32: LGTM!

The imports and route definition follow the established pattern from other solution routes. SEO metadata is complete with appropriate title, description, and Open Graph tags.


34-94: LGTM!

The features and useCases data arrays are well-structured with consistent schema. The content appropriately targets project management workflows.


158-216: LGTM!

The FeaturesSection and UseCasesSection are well-implemented with responsive grid layouts and proper React keys.


218-242: LGTM!

The CTA section is clean and follows the established pattern with proper cn() usage.


96-156: The review comment is incorrect and should be resolved.

The project uses Tailwind CSS v4.1.17, and bg-linear-to-b, bg-linear-to-t, and related gradient classes are the correct syntax for Tailwind v4 (not v3). The code in lines 96-156 properly implements these classes for the project's version. No changes are required.

apps/web/src/routes/_view/solution/healthcare.tsx (5)

8-31: Verify HIPAA-ready compliance claims.

The meta description claims "HIPAA-ready AI meeting notes" (line 14). Per the PR testing checklist, this claim should be verified. Consider whether "HIPAA-ready" accurately reflects the product's current compliance posture, or if softer language like "designed with healthcare privacy in mind" would be more appropriate until formal compliance is established.


34-71: LGTM!

The features array appropriately targets healthcare workflows with privacy-focused messaging. The "Self-Hosting Option" feature (lines 66-70) aligns with the self-hosting page being added in this PR.


73-110: LGTM!

The use cases appropriately cover healthcare meeting scenarios. The Component structure is consistent with other solution pages.


126-130: Verify privacy claim accuracy.

The hero section states "Your patient data never leaves your device" (lines 128-129). This claim should be verified to ensure accuracy across all usage scenarios. If the product offers both local and cloud processing options, consider clarifying the conditions under which data remains local.


159-245: LGTM!

The FeaturesSection, UseCasesSection, and CTASection are well-implemented and consistent with the established pattern across solution pages.

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
@devin-ai-integration devin-ai-integration bot changed the title Add SEO-ready pages: self-hosting, local-ai, /free, /company-handbook, and /solution pages Add SEO-ready pages and individual template pages with OG image support Nov 28, 2025
Copy link
Contributor

@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

🧹 Nitpick comments (4)
apps/web/src/routes/_view/templates/$slug.tsx (4)

10-21: Duplicate template lookup between beforeLoad and loader.

The template is looked up twice using allTemplates.find() in both beforeLoad (line 11) and loader (line 19). While the non-null assertion on line 20 is safe because beforeLoad guarantees the template exists (via redirect), this duplication is inefficient and violates DRY.

Consider consolidating the lookup logic if the router API allows, or at minimum, add a comment explaining why the duplication is necessary.


22-26: Consider breaking the long URL construction into multiple lines.

The ogImageUrl construction on line 25 is quite long and hard to read at a glance. For better readability, consider breaking it into multiple lines or extracting the query parameter construction.

Example refactor:

-    const ogImageUrl = `https://hyprnote.com/og?type=templates&title=${encodeURIComponent(template.title)}&category=${encodeURIComponent(template.category)}${template.description ? `&description=${encodeURIComponent(template.description)}` : ""}`;
+    const params = new URLSearchParams({
+      type: "templates",
+      title: template.title,
+      category: template.category,
+    });
+    if (template.description) {
+      params.set("description", template.description);
+    }
+    const ogImageUrl = `https://hyprnote.com/og?${params.toString()}`;

51-72: Consider moving icon mappings to template data.

The hardcoded icon mapping creates tight coupling between template titles and icon identifiers. If a template title changes, this code must be manually updated, which is error-prone and not scalable.

Consider adding an icon field to the template content-collection schema and storing the icon identifier directly in each template's frontmatter. This would eliminate the need for this mapping function and make the data more self-contained.

Additionally, if you keep this mapping, consider moving the iconMap object outside the function to avoid recreating it on every call:

+const TEMPLATE_ICON_MAP: Record<string, string> = {
+  "Daily Standup": "mdi:run-fast",
+  // ... rest of mappings
+};
+
 function getIconForTemplate(title: string): string {
-  const iconMap: Record<string, string> = {
-    "Daily Standup": "mdi:run-fast",
-    // ... 
-  };
-  return iconMap[title] || "mdi:file-document";
+  return TEMPLATE_ICON_MAP[title] || "mdi:file-document";
 }

162-162: Consider splitting the long className for better readability.

The className on line 162 is over 400 characters and spans multiple concerns (prose styling, code styling, spacing, borders, background). While the coding guideline specifically mentions using cn "with conditional logic" (which doesn't apply here), splitting this into logical groups would still improve readability.

Example refactor:

-            <article className="prose prose-stone prose-headings:font-serif prose-headings:font-semibold prose-h2:text-xl prose-h2:mt-8 prose-h2:mb-4 prose-a:text-stone-600 prose-a:underline prose-a:decoration-dotted hover:prose-a:text-stone-800 prose-code:bg-stone-50 prose-code:border prose-code:border-neutral-200 prose-code:rounded prose-code:px-1.5 prose-code:py-0.5 prose-code:text-sm prose-code:font-mono prose-code:text-stone-700 max-w-none p-6 border border-neutral-200 rounded-lg bg-stone-50/50">
+            <article className={cn([
+              // Base prose
+              "prose prose-stone max-w-none",
+              // Heading styles
+              "prose-headings:font-serif prose-headings:font-semibold",
+              "prose-h2:text-xl prose-h2:mt-8 prose-h2:mb-4",
+              // Link styles
+              "prose-a:text-stone-600 prose-a:underline prose-a:decoration-dotted",
+              "hover:prose-a:text-stone-800",
+              // Code styles
+              "prose-code:bg-stone-50 prose-code:border prose-code:border-neutral-200",
+              "prose-code:rounded prose-code:px-1.5 prose-code:py-0.5",
+              "prose-code:text-sm prose-code:font-mono prose-code:text-stone-700",
+              // Container styles
+              "p-6 border border-neutral-200 rounded-lg bg-stone-50/50",
+            ])}>
📜 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 2f23597 and 9d940e6.

⛔ Files ignored due to path filters (1)
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
📒 Files selected for processing (1)
  • apps/web/src/routes/_view/templates/$slug.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/templates/$slug.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/templates/$slug.tsx (1)
apps/web/src/components/download-button.tsx (1)
  • DownloadButton (5-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
Copy link
Contributor

@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

🧹 Nitpick comments (1)
apps/web/netlify/edge-functions/og.tsx (1)

241-257: Consider extracting a shared renderer to reduce duplication.

renderTemplatesTemplate is nearly identical to renderDocsTemplate (lines 223-239), differing only in the header text ("Meeting Templates" vs "Hyprnote Docs") and the parameter field name (category vs section).

Consider extracting a shared renderer function:

+function renderCardTemplate(
+  headerText: string,
+  sectionOrCategory: string,
+  title: string,
+  description?: string
+) {
+  return (
+    <div style={{ width: '100%', height: '100%', paddingLeft: 56, paddingRight: 56, paddingTop: 55, paddingBottom: 55, background: 'linear-gradient(0deg, #FAFAF9 0%, #E7E5E4 100%)', overflow: 'hidden', flexDirection: 'column', justifyContent: 'space-between', alignItems: 'flex-start', display: 'flex' }}>
+      <div style={{ justifyContent: 'flex-start', alignItems: 'center', gap: 12, display: 'flex' }}>
+        <img style={{ width: 48, height: 48 }} src="https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/icons/stable-icon.png" />
+        <div style={{ color: '#292524', fontSize: 36, fontFamily: 'Lora', fontWeight: '700', wordWrap: 'break-word' }}>{headerText}</div>
+      </div>
+      <div style={{ alignSelf: 'stretch', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', gap: 12, display: 'flex' }}>
+        <div style={{ color: '#525252', fontSize: 32, fontFamily: 'IBM Plex Mono', fontWeight: '500', wordWrap: 'break-word' }}>{sectionOrCategory}</div>
+        <div style={{ alignSelf: 'stretch', color: '#292524', fontSize: 60, fontFamily: 'Lora', fontWeight: '700', wordWrap: 'break-word' }}>{preventWidow(title)}</div>
+        {description && (
+          <div style={{ alignSelf: 'stretch', color: '#525252', fontSize: 36, fontFamily: 'IBM Plex Mono', fontWeight: '400', wordWrap: 'break-word' }}>{description}</div>
+        )}
+      </div>
+    </div>
+  );
+}
+
 function renderDocsTemplate(params: z.infer<typeof docsSchema>) {
-  return (
-    <div style={{ width: '100%', height: '100%', paddingLeft: 56, paddingRight: 56, paddingTop: 55, paddingBottom: 55, background: 'linear-gradient(0deg, #FAFAF9 0%, #E7E5E4 100%)', overflow: 'hidden', flexDirection: 'column', justifyContent: 'space-between', alignItems: 'flex-start', display: 'flex' }}>
-      <div style={{ justifyContent: 'flex-start', alignItems: 'center', gap: 12, display: 'flex' }}>
-        <img style={{ width: 48, height: 48 }} src="https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/icons/stable-icon.png" />
-        <div style={{ color: '#292524', fontSize: 36, fontFamily: 'Lora', fontWeight: '700', wordWrap: 'break-word' }}>Hyprnote Docs</div>
-      </div>
-      <div style={{ alignSelf: 'stretch', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', gap: 12, display: 'flex' }}>
-        <div style={{ color: '#525252', fontSize: 32, fontFamily: 'IBM Plex Mono', fontWeight: '500', wordWrap: 'break-word' }}>{params.section}</div>
-        <div style={{ alignSelf: 'stretch', color: '#292524', fontSize: 60, fontFamily: 'Lora', fontWeight: '700', wordWrap: 'break-word' }}>{preventWidow(params.title)}</div>
-        {params.description && (
-          <div style={{ alignSelf: 'stretch', color: '#525252', fontSize: 36, fontFamily: 'IBM Plex Mono', fontWeight: '400', wordWrap: 'break-word' }}>{params.description}</div>
-        )}
-      </div>
-    </div>
-  );
+  return renderCardTemplate("Hyprnote Docs", params.section, params.title, params.description);
 }

 function renderTemplatesTemplate(params: z.infer<typeof templatesSchema>) {
-  return (
-    <div style={{ width: '100%', height: '100%', paddingLeft: 56, paddingRight: 56, paddingTop: 55, paddingBottom: 55, background: 'linear-gradient(0deg, #FAFAF9 0%, #E7E5E4 100%)', overflow: 'hidden', flexDirection: 'column', justifyContent: 'space-between', alignItems: 'flex-start', display: 'flex' }}>
-      <div style={{ justifyContent: 'flex-start', alignItems: 'center', gap: 12, display: 'flex' }}>
-        <img style={{ width: 48, height: 48 }} src="https://ijoptyyjrfqwaqhyxkxj.supabase.co/storage/v1/object/public/public_images/icons/stable-icon.png" />
-        <div style={{ color: '#292524', fontSize: 36, fontFamily: 'Lora', fontWeight: '700', wordWrap: 'break-word' }}>Meeting Templates</div>
-      </div>
-      <div style={{ alignSelf: 'stretch', flexDirection: 'column', justifyContent: 'flex-start', alignItems: 'flex-start', gap: 12, display: 'flex' }}>
-        <div style={{ color: '#525252', fontSize: 32, fontFamily: 'IBM Plex Mono', fontWeight: '500', wordWrap: 'break-word' }}>{params.category}</div>
-        <div style={{ alignSelf: 'stretch', color: '#292524', fontSize: 60, fontFamily: 'Lora', fontWeight: '700', wordWrap: 'break-word' }}>{preventWidow(params.title)}</div>
-        {params.description && (
-          <div style={{ alignSelf: 'stretch', color: '#525252', fontSize: 36, fontFamily: 'IBM Plex Mono', fontWeight: '400', wordWrap: 'break-word' }}>{params.description}</div>
-        )}
-      </div>
-    </div>
-  );
+  return renderCardTemplate("Meeting Templates", params.category, params.title, params.description);
 }
📜 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 9d940e6 and 93e3878.

📒 Files selected for processing (1)
  • apps/web/netlify/edge-functions/og.tsx (5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/netlify/edge-functions/og.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: Pages changed - hyprnote
🔇 Additional comments (4)
apps/web/netlify/edge-functions/og.tsx (4)

6-17: LGTM! Well-structured schema definitions.

The separation of meetingSchema and templatesSchema with a discriminated union provides good type safety. The templatesSchema fields (title, category, optional description) are appropriate for template metadata.

Also applies to: 39-39


84-91: LGTM! Parsing logic is correct.

The templates type parsing correctly extracts and validates the required fields, with proper handling of the optional description parameter.


283-286: LGTM! Handler routing is correct.

The routing logic properly handles the new templates type and correctly falls back to renderMeetingTemplate for the meeting type.


289-289: LGTM! Font loading logic correctly updated.

Including "templates" in the needsCustomFonts check is correct since renderTemplatesTemplate uses the Lora and IBM Plex Mono fonts.

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
@devin-ai-integration devin-ai-integration bot changed the title Add SEO-ready pages and individual template pages with OG image support Add SEO-ready pages, template pages with OG images, and shortcuts collection Nov 28, 2025
Copy link
Contributor

@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

🧹 Nitpick comments (1)
apps/web/content-collections.ts (1)

442-501: Consider extracting shared transform logic.

The handbook collection transform duplicates nearly the entire docs collection transform (lines 131-191). Both perform identical operations: extractToc, compileMDX with the same plugins, path parsing, sectionFolder/isIndex/order computation, and slug generation.

Consider extracting a shared helper function to improve maintainability and ensure consistency when updating the transform logic.

Example refactor:

function createDocStyleTransform() {
  return async (document: any, context: any) => {
    const toc = extractToc(document.content);

    const mdx = await compileMDX(context, document, {
      remarkPlugins: [remarkGfm, mdxMermaid],
      rehypePlugins: [
        rehypeSlug,
        [
          rehypeAutolinkHeadings,
          {
            behavior: "wrap",
            properties: {
              className: ["anchor"],
            },
          },
        ],
      ],
    });

    const pathParts = document._meta.path.split("/");
    const fileName = pathParts.pop()!.replace(/\.mdx$/, "");
    const sectionFolder = pathParts[0] || "general";
    const isIndex = fileName === "index";
    const orderMatch = fileName.match(/^(\d+)\./);
    const order = orderMatch ? parseInt(orderMatch[1], 10) : 999;
    const cleanFileName = fileName.replace(/^\d+\./, "");
    const cleanPath =
      pathParts.length > 0
        ? `${pathParts.join("/")}/${cleanFileName}`
        : cleanFileName;
    const slug = cleanPath;

    return {
      ...document,
      mdx,
      slug,
      sectionFolder,
      isIndex,
      order,
      toc,
    };
  };
}

// Then use it for both collections
const docs = defineCollection({
  // ... schema ...
  transform: createDocStyleTransform(),
});

const handbook = defineCollection({
  // ... schema ...
  transform: createDocStyleTransform(),
});
📜 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 93e3878 and d5310b0.

📒 Files selected for processing (7)
  • apps/web/content-collections.ts (1 hunks)
  • apps/web/content/shortcuts/action-items.mdx (1 hunks)
  • apps/web/content/shortcuts/customer-concerns.mdx (1 hunks)
  • apps/web/content/shortcuts/decision-summary.mdx (1 hunks)
  • apps/web/content/shortcuts/follow-up-email.mdx (1 hunks)
  • apps/web/content/shortcuts/meeting-insights.mdx (1 hunks)
  • apps/web/content/shortcuts/sales-feedback.mdx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/web/content/shortcuts/customer-concerns.mdx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/content-collections.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/content-collections.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: fmt
  • GitHub Check: ci (macos, macos-14)
🔇 Additional comments (6)
apps/web/content/shortcuts/meeting-insights.mdx (1)

1-24: LGTM! Well-structured shortcut content.

The frontmatter and content sections are clear and consistent. The prompt is detailed with specific analysis points that will guide the AI effectively.

apps/web/content/shortcuts/follow-up-email.mdx (1)

1-24: LGTM! Clear and actionable email drafting guide.

The prompt provides specific requirements for follow-up emails with appropriate structure and tone guidance.

apps/web/content/shortcuts/decision-summary.mdx (1)

1-24: LGTM! Comprehensive decision documentation guide.

The prompt effectively covers all aspects of decision capture including context, reasoning, stakeholders, and dissenting opinions.

apps/web/content/shortcuts/action-items.mdx (1)

1-24: LGTM! Clear action item extraction guide.

The prompt includes all necessary details (task, owner, deadline, dependencies) and flags urgent items appropriately.

apps/web/content/shortcuts/sales-feedback.mdx (1)

1-24: LGTM! Comprehensive sales coaching guide.

The prompt covers all critical aspects of sales call analysis with actionable feedback focus.

apps/web/content-collections.ts (1)

404-440: LGTM! Clean shortcuts collection implementation.

The schema appropriately captures shortcut metadata, and the transform follows the established pattern used by other collections.

Co-Authored-By: john@hyprnote.com <john@hyprnote.com>
Copy link
Contributor

@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

🧹 Nitpick comments (4)
apps/web/src/routes/_view/company-handbook/-components.tsx (4)

17-23: Tighten doc typing to avoid any and make required fields explicit

Both HandbookLayout and ArticleHeader accept doc: any while relying on fields like sectionFolder, title, summary, mdx, toc, created, updated, and author. If you have a generated/content-collections type for handbook docs, wiring that through here would:

  • Prevent accidental usage with the wrong doc shape.
  • Make fields like sectionFolder and toc clearly required, avoiding potential runtime errors (e.g., calling .split or accessing .toc on undefined).

If a shared type exists (e.g., from content-collections), prefer using it instead of any; otherwise, a local minimal type for this layout would still be an improvement.

Also applies to: 37-51


68-95: Consider clarifying date expectations or guarding against invalid dates

doc.created/doc.updated are passed straight into new Date(...) and formatted. If those values ever deviate from a valid ISO/date string, this will silently render "Invalid Date".

If your handbook schema guarantees valid dates, this is fine; otherwise, you might:

  • Validate/normalize dates at content-layer level, or
  • Guard here (e.g., only render <time> when !isNaN(new Date(doc.created).getTime())).

Not blocking, but worth confirming the schema guarantees.


186-215: Make toc handling a bit more defensive to avoid accidental runtime errors

RightSidebar assumes toc is a non-empty array (toc.length > 0) and HandbookLayout passes doc.toc directly. If doc.toc were ever undefined or null (e.g., for a doc without generated TOC), this would throw.

Even if the current content schema always provides toc: [], a small guard would make this more robust, e.g.:

- <RightSidebar toc={doc.toc} />
+ <RightSidebar toc={doc.toc ?? []} />

and/or adjust the prop type to toc?: Array<...> and default to [] inside RightSidebar.


217-235: CTA card implementation is clean; minor note on routing choice

The “Talk to us” CTA uses an <a> with href="/founders" plus target="_blank" and rel="noopener noreferrer", which is correct technically. If you don’t specifically want a new tab, you could switch to <Link to="/founders"> for client-side navigation; otherwise this is fine as-is.

📜 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 d5310b0 and 6648dfe.

📒 Files selected for processing (1)
  • apps/web/src/routes/_view/company-handbook/-components.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/company-handbook/-components.tsx
🧬 Code graph analysis (1)
apps/web/src/routes/_view/company-handbook/-components.tsx (1)
packages/utils/src/cn.ts (1)
  • cn (20-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt
🔇 Additional comments (2)
apps/web/src/routes/_view/company-handbook/-components.tsx (2)

100-117: MDX rendering setup and component mapping look solid

The MDXContent usage with a curated components map and well-tuned prose classes is clean and composable. No functional issues stand out here.


120-183: Footer structure and links look correct and semantically appropriate

Back-link, legal links via Link, and external social links (GitHub, Discord, Twitter/X) are wired correctly with target="_blank" and rel="noopener noreferrer". No changes needed from a correctness or UX standpoint.

Copy link
Contributor

@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

📜 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 6648dfe and acf0b0f.

⛔ Files ignored due to path filters (1)
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
📒 Files selected for processing (2)
  • apps/web/content-collections.ts (2 hunks)
  • apps/web/src/routes/_view/templates/$slug.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/src/routes/_view/templates/$slug.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.ts: Agent implementations should use TypeScript and follow the established architectural patterns defined in the agent framework
Agent communication should use defined message protocols and interfaces

Files:

  • apps/web/content-collections.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/content-collections.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: fmt
🔇 Additional comments (2)
apps/web/content-collections.ts (2)

404-440: LGTM! Shortcuts collection is well-structured.

The schema and transform logic follow established patterns. The absence of TOC extraction is appropriate for short-form shortcut content.

Note: Per the PR objectives, no UI/route has been implemented for this collection yet.


554-554: LGTM! Collections properly exported.

Both shortcuts and handbook collections are correctly added to the configuration.

Also applies to: 558-558

Comment on lines +486 to +545
const handbook = defineCollection({
name: "handbook",
directory: "content/handbook",
include: "**/*.mdx",
exclude: "AGENTS.md",
schema: z.object({
title: z.string(),
section: z.string(),
summary: z.string().optional(),
author: z.string().optional(),
created: z.string().optional(),
updated: z.string().optional(),
}),
transform: async (document, context) => {
const toc = extractToc(document.content);

const mdx = await compileMDX(context, document, {
remarkPlugins: [remarkGfm, mdxMermaid],
rehypePlugins: [
rehypeSlug,
[
rehypeAutolinkHeadings,
{
behavior: "wrap",
properties: {
className: ["anchor"],
},
},
],
],
});

const pathParts = document._meta.path.split("/");
const fileName = pathParts.pop()!.replace(/\.mdx$/, "");

const sectionFolder = pathParts[0] || "general";

const isIndex = fileName === "index";

const orderMatch = fileName.match(/^(\d+)\./);
const order = orderMatch ? parseInt(orderMatch[1], 10) : 999;

const cleanFileName = fileName.replace(/^\d+\./, "");
const cleanPath =
pathParts.length > 0
? `${pathParts.join("/")}/${cleanFileName}`
: cleanFileName;
const slug = cleanPath;

return {
...document,
mdx,
slug,
sectionFolder,
isIndex,
order,
toc,
};
},
});
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Extract shared transform logic with docs collection.

The handbook transform (lines 499-544) duplicates nearly all the logic from the docs collection transform (lines 145-189). Both compute toc, pathParts, fileName, sectionFolder, isIndex, order, and slug identically.

Consider extracting the shared transform logic into a helper function:

+function createDocsLikeTransform() {
+  return async (document: any, context: any) => {
+    const toc = extractToc(document.content);
+
+    const mdx = await compileMDX(context, document, {
+      remarkPlugins: [remarkGfm, mdxMermaid],
+      rehypePlugins: [
+        rehypeSlug,
+        [
+          rehypeAutolinkHeadings,
+          {
+            behavior: "wrap",
+            properties: {
+              className: ["anchor"],
+            },
+          },
+        ],
+      ],
+    });
+
+    const pathParts = document._meta.path.split("/");
+    const fileName = pathParts.pop()!.replace(/\.mdx$/, "");
+
+    const sectionFolder = pathParts[0] || "general";
+
+    const isIndex = fileName === "index";
+
+    const orderMatch = fileName.match(/^(\d+)\./);
+    const order = orderMatch ? parseInt(orderMatch[1], 10) : 999;
+
+    const cleanFileName = fileName.replace(/^\d+\./, "");
+    const cleanPath =
+      pathParts.length > 0
+        ? `${pathParts.join("/")}/${cleanFileName}`
+        : cleanFileName;
+    const slug = cleanPath;
+
+    return {
+      ...document,
+      mdx,
+      slug,
+      sectionFolder,
+      isIndex,
+      order,
+      toc,
+    };
+  };
+}

Then use it in both collections:

 const docs = defineCollection({
   name: "docs",
   directory: "content/docs",
   include: "**/*.mdx",
   exclude: "AGENTS.md",
   schema: z.object({
     title: z.string(),
     section: z.string(),
     summary: z.string().optional(),
     category: z.string().optional(),
     author: z.string().optional(),
     created: z.string().optional(),
     updated: z.string().optional(),
   }),
-  transform: async (document, context) => {
-    // ... duplicated logic
-  },
+  transform: createDocsLikeTransform(),
 });

 const handbook = defineCollection({
   name: "handbook",
   directory: "content/handbook",
   include: "**/*.mdx",
   exclude: "AGENTS.md",
   schema: z.object({
     title: z.string(),
     section: z.string(),
     summary: z.string().optional(),
     author: z.string().optional(),
     created: z.string().optional(),
     updated: z.string().optional(),
   }),
-  transform: async (document, context) => {
-    // ... duplicated logic
-  },
+  transform: createDocsLikeTransform(),
 });

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@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

📜 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 acf0b0f and 85a958b.

⛔ Files ignored due to path filters (1)
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
📒 Files selected for processing (1)
  • apps/web/src/routes/_view/templates/$slug.tsx (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Avoid creating a bunch of types/interfaces if they are not shared. Especially for function props, just inline them instead.
Never do manual state management for form/mutation. Use useForm (from tanstack-form) and useQuery/useMutation (from tanstack-query) instead for 99% of cases. Avoid patterns like setError.
If there are many classNames with conditional logic, use cn (import from @hypr/utils). It is similar to clsx. Always pass an array and split by logical grouping.
Use motion/react instead of framer-motion.

Files:

  • apps/web/src/routes/_view/templates/$slug.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: fmt
  • GitHub Check: Redirect rules - hyprnote
  • GitHub Check: Header rules - hyprnote
  • GitHub Check: Pages changed - hyprnote
🔇 Additional comments (1)
apps/web/src/routes/_view/templates/$slug.tsx (1)

7-7: LGTM: Import follows coding guidelines.

The cn utility import is correctly used later in the file (lines 272-277) with an array of classNames split by logical grouping, which aligns with the coding guidelines.

Comment on lines 23 to +24
const url = `https://hyprnote.com/templates/${template.slug}`;

const ogImageUrl = `https://hyprnote.com/og?type=templates&title=${encodeURIComponent(template.title)}&category=${encodeURIComponent(template.category)}${template.description ? `&description=${encodeURIComponent(template.description)}` : ""}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Hardcoded production URL affects all environments.

Both url and ogImageUrl use the hardcoded production domain https://hyprnote.com, which means OG images and canonical URLs will point to production even in development and staging environments. This makes local testing of OG images difficult and breaks environment parity.

Additionally, line 24 is quite long and difficult to read.

Consider extracting the base URL to a constant or environment variable and refactoring the URL construction:

+const baseUrl = process.env.PUBLIC_URL || 'https://hyprnote.com';
+
 export const Route = createFileRoute("/_view/templates/$slug")({
   component: Component,
   loader: async ({ params }) => {
     const template = allTemplates.find(
       (template) => template.slug === params.slug,
     );
     if (!template) {
       throw notFound();
     }
     return { template };
   },
   head: ({ loaderData }) => {
     const { template } = loaderData!;
-    const url = `https://hyprnote.com/templates/${template.slug}`;
+    const url = `${baseUrl}/templates/${template.slug}`;

-    const ogImageUrl = `https://hyprnote.com/og?type=templates&title=${encodeURIComponent(template.title)}&category=${encodeURIComponent(template.category)}${template.description ? `&description=${encodeURIComponent(template.description)}` : ""}`;
+    const ogImageParams = new URLSearchParams({
+      type: 'templates',
+      title: template.title,
+      category: template.category,
+    });
+    if (template.description) {
+      ogImageParams.set('description', template.description);
+    }
+    const ogImageUrl = `${baseUrl}/og?${ogImageParams}`;

     return {

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +28 to +44
{ title: `${template.title} - Meeting Template - Hyprnote` },
{ name: "description", content: template.description },
{ property: "og:title", content: `${template.title} Template` },
{
property: "og:title",
content: `${template.title} - Meeting Template`,
},
{ property: "og:description", content: template.description },
{ property: "og:type", content: "website" },
{ property: "og:type", content: "article" },
{ property: "og:url", content: url },
{ name: "twitter:card", content: "summary" },
{ name: "twitter:title", content: `${template.title} Template` },
{ property: "og:image", content: ogImageUrl },
{ name: "twitter:card", content: "summary_large_image" },
{
name: "twitter:title",
content: `${template.title} - Meeting Template`,
},
{ name: "twitter:description", content: template.description },
{ name: "twitter:image", content: ogImageUrl },
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Open Graph protocol og:type article best practices for product pages

💡 Result:

Summary — do NOT use og:type=article for standard product pages. Use product (or website) and product:* properties; reserve article only for editorial/blog/news content about the product.

Recommended best practices for product pages

  • Set og:type to product (or website for generic landing pages). Article is for blog/news/editorial only. [1][2]
  • Include canonical og:url and og:site_name to avoid duplicate/share confusion. [1][3]
  • Provide a large, high‑quality og:image (recommended 1200×630 px, 1.91:1) and include og:image:width / og:image:height and og:image:alt. [4][5]
  • Add product-specific Open Graph properties (product:retailer_item_id, product:availability, product:price:amount / product:price:currency, product:product_link, etc.) when relevant so platforms and integrations can surface commerce data. [2][3]
  • Keep og:title and og:description concise and aligned with on‑page content; test how they truncate on platforms. [4][5]
  • Ensure meta tags are present in the server‑rendered HTML (social crawlers do not execute client JS). [5]
  • Use the platform validators (Facebook Sharing Debugger, LinkedIn Post Inspector, Twitter Card Validator) to preview and force a rescrape after changes. [4]
  • If the page includes editorial (reviews, press release, blog post) use og:type=article and add article:* fields (published_time, modified_time, author, tag). Otherwise do not mark product pages as article. [1][2]

Sources

  • Open Graph / Facebook guidance and common og:type values (product, article, website). [1][3]
  • Yoast developer docs: typical site mappings (article for posts, website/home, profile pages). [2]
  • Example e‑commerce implementations and product:* usage (platform docs / examples). [3]
  • OG image and general tag best practices (sizes, dimensions, validators). [4][5]

🏁 Script executed:

#!/bin/bash
# Get context about the template page to understand what it represents
head -50 apps/web/src/routes/_view/templates/\$slug.tsx

Repository: fastrepl/hyprnote

Length of output: 1931


Change og:type to "product" or "website" — "article" is reserved for editorial content.

According to Open Graph best practices, og:type="article" should only be used for editorial, blog, or news content. For product and template showcase pages like this one, use og:type="product" (or "website" for generic landing pages) instead. This affects how social platforms categorize and display the page.

Consider also adding product-specific Open Graph properties (e.g., product:availability, product:price:amount) if commerce data is relevant.

🤖 Prompt for AI Agents
In apps/web/src/routes/_view/templates/$slug.tsx around lines 28 to 44, the Open
Graph meta currently sets og:type to "article" which is reserved for editorial
content; change the og:type value to either "product" (preferred for
templates/products) or "website" (for a generic landing page) by replacing the
property value, and optionally add product-specific OG properties (e.g.,
product:availability, product:price:amount, product:brand) where relevant to
improve social platform categorization and display.

@ComputelessComputer ComputelessComputer merged commit 3bd0f6f into main Nov 28, 2025
10 of 11 checks passed
@ComputelessComputer ComputelessComputer deleted the devin/1764315131-seo-landing-pages branch November 28, 2025 17:32
@coderabbitai coderabbitai bot mentioned this pull request Dec 1, 2025
5 tasks
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.

2 participants