feat: add /compare page — BYOA agent plugins vs dashboards#40
feat: add /compare page — BYOA agent plugins vs dashboards#40sidneyswift wants to merge 2 commits into
Conversation
- 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)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 51 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
| function CopyButton({ text }: { text: string }) { | ||
| const [copied, setCopied] = useState(false); | ||
| return ( | ||
| <button |
There was a problem hiding this comment.
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>
| import { compareCopy } from "@/lib/copy/compare"; | ||
|
|
||
| /* ── reveal-on-scroll ── */ | ||
| function useReveal() { |
There was a problem hiding this comment.
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>
| @@ -0,0 +1,156 @@ | |||
| "use client"; | |||
There was a problem hiding this comment.
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>
| import type { Plugin } from "@/lib/copy/plugins"; | ||
|
|
||
| /* ── reveal-on-scroll ── */ | ||
| function useReveal() { |
There was a problem hiding this comment.
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>
| {/* Header */} | ||
| <div className="flex items-start justify-between mb-4"> | ||
| <div> | ||
| <h3 className="text-xl font-semibold text-white">{plugin.name}</h3> |
There was a problem hiding this comment.
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>
| return ( | ||
| <button | ||
| onClick={() => { | ||
| navigator.clipboard.writeText(text); |
There was a problem hiding this comment.
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>
What
Adds a
/comparepage to the marketing site that positions Recoup's agent-native plugin approach against traditional dashboard tools.Why
What's included
app/compare/page.tsx— client component with reveal-on-scroll animationsapp/compare/layout.tsx— SEO metadatalib/copy/compare.ts— single source of truth for all copyVerification
Summary by cubic
Adds a new
/comparepage that explains why agent-native BYOA plugins beat dashboards, plus a/pluginsmarketplace page with one-command installs. Updates navigation and centralizes copy to support faster edits./comparemarketing page with an 8-row comparison table, “Why it matters” section, and CTAs to/pluginsand/pricing./pluginspage showcasing 4 plugins with capabilities, GitHub links, copy-to-clipboard install commands, and a simple “How it works” flow.lib/copy/compare.tsandlib/copy/plugins.ts.Written for commit d4ea588. Summary will update on new commits.