Skip to content

feat: add /compare page — BYOA agent plugins vs dashboards#40

Open
sidneyswift wants to merge 2 commits into
mainfrom
feat/compare-page
Open

feat: add /compare page — BYOA agent plugins vs dashboards#40
sidneyswift wants to merge 2 commits into
mainfrom
feat/compare-page

Conversation

@sidneyswift

@sidneyswift sidneyswift commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

What

Adds a /compare page to the marketing site that positions Recoup's agent-native plugin approach against traditional dashboard tools.

Why

  • Directly supports sales conversations (90-day revenue plan references needing this framing)
  • Prospects visiting the site can immediately understand why BYOA > another dashboard
  • Competitive landscape shows nobody else in music AI is agent-native — this page makes that clear
  • Complements the /plugins page (PR feat: /plugins page — agent plugin marketplace showcase #39) — "/plugins shows what we have, /compare shows why it matters"

What's included

  • app/compare/page.tsx — client component with reveal-on-scroll animations
  • app/compare/layout.tsx — SEO metadata
  • lib/copy/compare.ts — single source of truth for all copy
  • Nav updated: Compare added to header nav + footer under Resources
  • 8-row comparison table (where it runs, workflow, integration, content, research, adoption, scalability, lock-in)
  • "Why this matters for music companies" section with 3 cards
  • CTA to /plugins and /pricing

Verification

  • ✅ Next.js compiles successfully (pre-existing TS error in demos page is unrelated)
  • ✅ Follows existing design patterns (shadow-as-border, reveal animations, copy structure)

Summary by cubic

Adds a new /compare page that explains why agent-native BYOA plugins beat dashboards, plus a /plugins marketplace page with one-command installs. Updates navigation and centralizes copy to support faster edits.

  • New Features
    • /compare marketing page with an 8-row comparison table, “Why it matters” section, and CTAs to /plugins and /pricing.
    • /plugins page showcasing 4 plugins with capabilities, GitHub links, copy-to-clipboard install commands, and a simple “How it works” flow.
    • SEO metadata layouts for both pages and reveal-on-scroll animations following existing patterns.
    • Header and footer nav updated; page copy moved to lib/copy/compare.ts and lib/copy/plugins.ts.

Written for commit d4ea588. Summary will update on new commits.

Review in cubic

Cosmo and others added 2 commits July 5, 2026 20:55
- New /plugins page showcasing all 4 Recoup plugins with install commands
- Plugin cards with capabilities, skill counts, audience, GitHub links
- Copy-to-clipboard install commands for Claude Code/Cowork/Codex/Cursor
- How it works section (Install → Connect → Go)
- CTA to docs and pricing
- Added Plugins to header nav and footer product section
- Copy extracted to lib/copy/plugins.ts (single source of truth pattern)
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
marketing Ready Ready Preview Jul 6, 2026 1:04am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@sidneyswift, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7a19f94c-db30-4511-80a5-7b2ddd369e80

📥 Commits

Reviewing files that changed from the base of the PR and between f9862ee and d4ea588.

📒 Files selected for processing (7)
  • app/compare/layout.tsx
  • app/compare/page.tsx
  • app/plugins/layout.tsx
  • app/plugins/page.tsx
  • lib/copy/compare.ts
  • lib/copy/plugins.ts
  • lib/nav.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/compare-page

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

❤️ Share

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

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

6 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/plugins/page.tsx">

<violation number="1" location="app/plugins/page.tsx:10">
P2: The `useReveal` IntersectionObserver hook is copy-pasted in this file and 4 other files across the codebase. Since this is the 5th copy of the identical implementation, extracting it to a shared hook (e.g., `lib/hooks/useReveal.ts`) would remove the duplication and make future scroll-reveal changes consistent across pages. The implementation is stable and well-proven, so this is a maintainability improvement rather than a functional fix.</violation>

<violation number="2" location="app/plugins/page.tsx:40">
P2: `CopyButton` uses only `title="Copy install command"` for its accessible name, but `title` on buttons is inconsistently announced across screen readers. Add `aria-label="Copy install command"` so assistive tech reliably identifies the action.</violation>

<violation number="3" location="app/plugins/page.tsx:42">
P3: `navigator.clipboard.writeText` can reject (insecure context, permission denied, unavailable API), but the handler has no `.catch()` — a rejected promise silently fails while the button still shows a green checkmark. Wrapping in a try/catch with a fallback (or at minimum a console.warn for analytics) would prevent a false-positive success state.</violation>

<violation number="4" location="app/plugins/page.tsx:71">
P3: The plugin grid section uses h3 card headings directly after the page h1 without an intervening h2. Screen reader users navigating by heading will jump from the h1 to h3 with no section-level heading. Adding a visually-hidden h2 above the grid (e.g., "Available Plugins") would fix the hierarchy without changing the visual layout.</violation>
</file>

<file name="app/compare/page.tsx">

<violation number="1" location="app/compare/page.tsx:9">
P2: The `useReveal` hook is defined locally in this file but already exists in 3 other pages (/, /plugins, /results) with identical logic. Extracting it to `lib/hooks/useReveal.ts` would align with the project's explicit "no duplication" principle and make future animation tuning apply across all pages at once.</violation>

<violation number="2" location="app/compare/page.tsx:65">
P2: The comparison table is tabular data (8 category rows × 3 columns) rendered as `<div>` elements with CSS grid. Screen readers will not announce column/row relationships, making the key competitive comparison invisible to assistive tech users. Refactor to `<table>` with `<th>` scoped headers, or at minimum add `role="table"`, `role="rowgroup"`, `role="row"`, `role="columnheader"`, and `role="cell"` to preserve the data structure for accessibility.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/plugins/page.tsx
function CopyButton({ text }: { text: string }) {
const [copied, setCopied] = useState(false);
return (
<button

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: CopyButton uses only title="Copy install command" for its accessible name, but title on buttons is inconsistently announced across screen readers. Add aria-label="Copy install command" so assistive tech reliably identifies the action.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/plugins/page.tsx, line 40:

<comment>`CopyButton` uses only `title="Copy install command"` for its accessible name, but `title` on buttons is inconsistently announced across screen readers. Add `aria-label="Copy install command"` so assistive tech reliably identifies the action.</comment>

<file context>
@@ -0,0 +1,234 @@
+function CopyButton({ text }: { text: string }) {
+  const [copied, setCopied] = useState(false);
+  return (
+    <button
+      onClick={() => {
+        navigator.clipboard.writeText(text);
</file context>

Comment thread app/compare/page.tsx
import { compareCopy } from "@/lib/copy/compare";

/* ── reveal-on-scroll ── */
function useReveal() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The useReveal hook is defined locally in this file but already exists in 3 other pages (/, /plugins, /results) with identical logic. Extracting it to lib/hooks/useReveal.ts would align with the project's explicit "no duplication" principle and make future animation tuning apply across all pages at once.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/compare/page.tsx, line 9:

<comment>The `useReveal` hook is defined locally in this file but already exists in 3 other pages (/, /plugins, /results) with identical logic. Extracting it to `lib/hooks/useReveal.ts` would align with the project's explicit "no duplication" principle and make future animation tuning apply across all pages at once.</comment>

<file context>
@@ -0,0 +1,156 @@
+import { compareCopy } from "@/lib/copy/compare";
+
+/* ── reveal-on-scroll ── */
+function useReveal() {
+  const ref = useRef<HTMLDivElement>(null);
+  const [v, setV] = useState(false);
</file context>

Comment thread app/compare/page.tsx
@@ -0,0 +1,156 @@
"use client";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The comparison table is tabular data (8 category rows × 3 columns) rendered as <div> elements with CSS grid. Screen readers will not announce column/row relationships, making the key competitive comparison invisible to assistive tech users. Refactor to <table> with <th> scoped headers, or at minimum add role="table", role="rowgroup", role="row", role="columnheader", and role="cell" to preserve the data structure for accessibility.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/compare/page.tsx, line 65:

<comment>The comparison table is tabular data (8 category rows × 3 columns) rendered as `<div>` elements with CSS grid. Screen readers will not announce column/row relationships, making the key competitive comparison invisible to assistive tech users. Refactor to `<table>` with `<th>` scoped headers, or at minimum add `role="table"`, `role="rowgroup"`, `role="row"`, `role="columnheader"`, and `role="cell"` to preserve the data structure for accessibility.</comment>

<file context>
@@ -0,0 +1,156 @@
+      {/* ── Comparison Table ── */}
+      <div ref={table.ref} className={table.cls}>
+        <section className="mb-20">
+          <div className="rounded-lg border border-[var(--border)] overflow-hidden">
+            {/* Header */}
+            <div className="grid grid-cols-[1fr_1fr_1fr] bg-[var(--foreground)]/5">
</file context>

Comment thread app/plugins/page.tsx
import type { Plugin } from "@/lib/copy/plugins";

/* ── reveal-on-scroll ── */
function useReveal() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The useReveal IntersectionObserver hook is copy-pasted in this file and 4 other files across the codebase. Since this is the 5th copy of the identical implementation, extracting it to a shared hook (e.g., lib/hooks/useReveal.ts) would remove the duplication and make future scroll-reveal changes consistent across pages. The implementation is stable and well-proven, so this is a maintainability improvement rather than a functional fix.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/plugins/page.tsx, line 10:

<comment>The `useReveal` IntersectionObserver hook is copy-pasted in this file and 4 other files across the codebase. Since this is the 5th copy of the identical implementation, extracting it to a shared hook (e.g., `lib/hooks/useReveal.ts`) would remove the duplication and make future scroll-reveal changes consistent across pages. The implementation is stable and well-proven, so this is a maintainability improvement rather than a functional fix.</comment>

<file context>
@@ -0,0 +1,234 @@
+import type { Plugin } from "@/lib/copy/plugins";
+
+/* ── reveal-on-scroll ── */
+function useReveal() {
+  const ref = useRef<HTMLDivElement>(null);
+  const [v, setV] = useState(false);
</file context>

Comment thread app/plugins/page.tsx
{/* Header */}
<div className="flex items-start justify-between mb-4">
<div>
<h3 className="text-xl font-semibold text-white">{plugin.name}</h3>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The plugin grid section uses h3 card headings directly after the page h1 without an intervening h2. Screen reader users navigating by heading will jump from the h1 to h3 with no section-level heading. Adding a visually-hidden h2 above the grid (e.g., "Available Plugins") would fix the hierarchy without changing the visual layout.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/plugins/page.tsx, line 71:

<comment>The plugin grid section uses h3 card headings directly after the page h1 without an intervening h2. Screen reader users navigating by heading will jump from the h1 to h3 with no section-level heading. Adding a visually-hidden h2 above the grid (e.g., "Available Plugins") would fix the hierarchy without changing the visual layout.</comment>

<file context>
@@ -0,0 +1,234 @@
+        {/* Header */}
+        <div className="flex items-start justify-between mb-4">
+          <div>
+            <h3 className="text-xl font-semibold text-white">{plugin.name}</h3>
+            <span className="text-xs text-neutral-500 mt-1 block">
+              {plugin.skillCount} skill{plugin.skillCount !== 1 ? "s" : ""}
</file context>

Comment thread app/plugins/page.tsx
return (
<button
onClick={() => {
navigator.clipboard.writeText(text);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: navigator.clipboard.writeText can reject (insecure context, permission denied, unavailable API), but the handler has no .catch() — a rejected promise silently fails while the button still shows a green checkmark. Wrapping in a try/catch with a fallback (or at minimum a console.warn for analytics) would prevent a false-positive success state.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/plugins/page.tsx, line 42:

<comment>`navigator.clipboard.writeText` can reject (insecure context, permission denied, unavailable API), but the handler has no `.catch()` — a rejected promise silently fails while the button still shows a green checkmark. Wrapping in a try/catch with a fallback (or at minimum a console.warn for analytics) would prevent a false-positive success state.</comment>

<file context>
@@ -0,0 +1,234 @@
+  return (
+    <button
+      onClick={() => {
+        navigator.clipboard.writeText(text);
+        setCopied(true);
+        setTimeout(() => setCopied(false), 2000);
</file context>

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.

1 participant