Skip to content

feat(ui): add public /apps leaderboard page - #2170

Merged
smakosh merged 8 commits into
mainfrom
feat/apps-page
May 8, 2026
Merged

smakosh merged 8 commits into
mainfrom
feat/apps-page

Conversation

@smakosh

@smakosh smakosh commented May 5, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Adds a public /apps page that lists tools and coding agents using LLM Gateway, ranked by tokens processed (similar to openrouter.ai/apps)
  • Aggregation comes from a new public endpoint GET /public/apps that groups log.source and sums total_tokens
  • Seed data expanded with popular coding agents — Claude Code, Cursor, Cline, Codex, OpenCode, Aider, Continue, Windsurf, Roo, Kilo, Zed, Bolt, v0, Lovable, Autohand, SoulForge, OpenClaw, n8n — and log volume bumped so the leaderboard has realistic ranks
  • Page features a top-3 podium with blue-accented feat: migrate to tanstack start and add all dashboard pages #1, a top-12 grid, a table-style long-tail list, search + category filters, and a DevPass upsell as the closing section

Design notes

Aesthetic intentionally matches the existing landing-page DNA in apps/ui/src/components/landing/*: font-display headlines, AnimatedGroup blur-slide reveals, atmospheric blue radial blobs, gradient hairline separators, single ShimmerButton primary CTA. No purple-gradient AI-template tropes.

The DevPass upsell uses logo-stack social proof from the leaderboard above (Claude Code, Cursor, Cline, OpenCode) and loss-aversion framing ("Stop juggling nine separate subscriptions") rather than generic feature bullets.

Test plan

  • pnpm run setup to reseed; visit http://localhost:3002/apps
  • Verify the top 3 podium shows the highest-volume seeded agents
  • Verify the search box filters by display name and source slug
  • Verify the category pills (All / Coding agents / Automation / Other) filter correctly
  • Verify the long-tail list collapses below the top 12
  • Verify GET /public/apps returns aggregated stats without auth
  • Verify GET /public/apps?limit=5 respects the limit
  • Verify the DevPass upsell links to https://devpass.llmgateway.io
  • Verify dark mode looks correct (atmospheric glow, podium accent)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Public "Apps" page with hero metrics (apps tracked, tokens processed, requests routed), ISR, and "Read the docs" link.
    • Searchable, category-filterable apps leaderboard with top-3 podium, results grid, and ranked list.
    • Public API endpoint providing aggregated app traffic metrics for the UI.
    • App metadata for richer app cards (names, descriptions, icons/links).
    • DevPass upsell hero with CTAs and example setup snippet.
  • Chores
    • Seed data expanded to generate richer, higher-volume demo activity for public metrics.

Lists apps using LLM Gateway ranked by tokens processed, aggregated
from the log.source field. Public endpoint at /public/apps. Seed data
expanded to cover popular coding agents (Claude Code, Cursor, Cline,
Codex, OpenCode, Aider, Continue, Windsurf, Roo, Kilo, Zed, Bolt, v0,
Lovable, Autohand, SoulForge, OpenClaw, n8n) so the page has realistic
ranks. Page features a top-3 podium, top-12 grid, long-tail list, and
a DevPass upsell.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 5, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ab8efe67-19fa-4fc9-b367-7d057dd3668d

📥 Commits

Reviewing files that changed from the base of the PR and between 7a996fe and 02f06d3.

⛔ Files ignored due to path filters (4)
  • apps/code/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
  • apps/playground/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
  • apps/ui/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
  • ee/admin/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
📒 Files selected for processing (1)
  • apps/api/src/routes/public-apps.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/routes/public-apps.ts

Walkthrough

Adds a public apps aggregation API (/public/apps), a Next.js page that fetches and renders ranked app usage with search/filter UI and upsell content, plus updated DB seed logic to produce weighted agent activity for the public listing.

Changes

Public Apps Leaderboard

Layer / File(s) Summary
API schema & OpenAPI route
apps/api/src/routes/public-apps.ts
Declare publicApps OpenAPI app, appStatSchema, and GET / route with validated limit query and response schema.
API handler & DB aggregation
apps/api/src/routes/public-apps.ts
Query globalSourceStats aggregated by source (SUM tokens, SUM requests, MAX dayTimestamp), map rows to JSON-friendly stats, compute totals.
API registration
apps/api/src/index.ts
Import publicApps and register it at app.route("/public/apps", publicApps).
UI metadata & types
apps/ui/src/components/apps/app-metadata.ts
Add AppMetadata interface, APP_METADATA record of known sources, and getAppMetadata() fallback.
AppsGrid helpers
apps/ui/src/components/apps/apps-grid.tsx
Add token formatting and relative-time helpers used by grid and rows.
App logo rendering
apps/ui/src/components/apps/apps-grid.tsx
AppLogo renders SVG icon or fallback initial from source/displayName.
Podium (top-3) card
apps/ui/src/components/apps/apps-grid.tsx
PodiumCard for top-3 with progress bar, metadata, and optional external links.
Grid card (mid results)
apps/ui/src/components/apps/apps-grid.tsx
GridCard compact mid-list card with progress and metadata.
List row (long tail)
apps/ui/src/components/apps/apps-grid.tsx
ListRow responsive ranked row with rank, logo, tokens, requests, and last-seen.
AppsGrid main component
apps/ui/src/components/apps/apps-grid.tsx
Controlled query/category state, enrich with getAppMetadata, filter/rank, and render podium/grid/list or empty-state.
DevPass upsell component
apps/ui/src/components/apps/devpass-upsell.tsx
Client hero upsell with badges, headline, CTAs, and code-sample panel.
Page integration
apps/ui/src/app/apps/page.tsx
Next.js server page exports metadata, revalidate = 300, fetches /public/apps?limit=200, formats totals, renders hero stats, AppsGrid or empty state, DevPassUpsell, and Footer.
Seed data
packages/db/src/seed.ts
Expand DEVPASS_AGENTS, increase DEVPASS_LOG_COUNT to 1800, compute DEVPASS_WEIGHT_TOTAL, and switch to scaled cumulative-weight sampling for agent selection.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant NextPage as Next.js Page
    participant APIServer as API Server
    participant Database
    participant AppsGrid as AppsGrid Component

    Browser->>NextPage: Request /apps
    NextPage->>APIServer: GET /public/apps?limit=200
    APIServer->>Database: Aggregate globalSourceStats by source
    Database-->>APIServer: grouped rows (tokens, requests, lastUsedAt)
    APIServer-->>NextPage: JSON { apps, totalApps, totalTokens, totalRequests }
    NextPage->>NextPage: compute totals & format numbers
    NextPage->>AppsGrid: pass apps[]
    AppsGrid->>AppsGrid: enrich (getAppMetadata), filter, rank
    AppsGrid-->>NextPage: render podium, grid, list
    NextPage-->>Browser: HTML (hero, grid, upsell)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(ui): add public /apps leaderboard page' directly and clearly describes the main change: adding a new public leaderboard page for apps at /apps with a focus on the UI component.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/apps-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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (1)
apps/ui/src/app/apps/page.tsx (1)

39-50: ⚡ Quick win

formatBigNumber duplicates formatTokens in apps-grid.tsx.

Both functions are character-for-character identical (B/M/K suffix thresholds, same Intl.NumberFormat fallback). Extract to a shared utility (e.g., apps/ui/src/lib/format.ts) and import in both files.

♻️ Proposed refactor

Create apps/ui/src/lib/format.ts:

const numberFormatter = new Intl.NumberFormat("en-US");

export function formatTokens(n: number): string {
	if (n >= 1_000_000_000) {
		return `${(n / 1_000_000_000).toFixed(2)}B`;
	}
	if (n >= 1_000_000) {
		return `${(n / 1_000_000).toFixed(2)}M`;
	}
	if (n >= 1_000) {
		return `${(n / 1_000).toFixed(1)}K`;
	}
	return numberFormatter.format(n);
}

Then in both page.tsx and apps-grid.tsx:

-function formatBigNumber(n: number): string { … }  // page.tsx
-function formatTokens(n: number): string { … }     // apps-grid.tsx
+import { formatTokens } from "@/lib/format";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/ui/src/app/apps/page.tsx` around lines 39 - 50, formatBigNumber in
page.tsx is duplicated by formatTokens in apps-grid.tsx; extract the shared
logic into a single exported utility (e.g., export function formatTokens /
formatBigNumber in a new shared module) that defines the common numberFormatter
and the B/M/K rules, then replace both local implementations by importing and
calling that shared function from page.tsx and apps-grid.tsx (update references
to formatBigNumber or formatTokens to the unified export).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/api/src/routes/public-apps.ts`:
- Around line 50-63: The query in public-apps.ts aggregates the entire
tables.log (select/groupBy/orderBy on tables.log.source) causing full table
scans; fix by 1) restricting the query with a date-range filter on
tables.log.createdAt (e.g., only include rows where createdAt >= now() -
interval '90 days') so the GROUP BY and aggregates operate on a bounded window,
2) add an index on source in your DB schema (e.g., in packages/db/src/schema.ts
add an index named like log_source_idx on table.source) so GROUP BY/ORDER BY can
use an index scan, and 3) return a Cache-Control header from this
unauthenticated route so CDN/proxies can cache responses (set appropriate
max-age/stale-while-revalidate) to avoid repeated expensive scans when clients
hit /public/apps?limit=...

In `@apps/ui/src/app/apps/page.tsx`:
- Around line 164-170: Replace the plain anchor used for internal navigation
with Next's Link: import Link from 'next/link' at the top of page.tsx and change
the <a href="/docs" ...>...</a> to <Link href="/docs" className="group
inline-flex items-center gap-1.5 text-sm font-medium text-foreground
hover:text-blue-500">Read the docs <ArrowRight .../></Link>, leaving the
ArrowRight component and className intact so client-side routing, prefetching,
and scroll restoration are used.

In `@apps/ui/src/components/apps/apps-grid.tsx`:
- Line 393: The ternary assigning listApps is redundant because both branches
use filtered.slice(12); replace the expression with a single direct assignment
(set listApps = filtered.slice(12)) and remove the conditional isFiltered ? ...
: ...; update the declaration that currently references listApps, isFiltered,
and filtered.slice(12) to the simplified form so the code is clearer and avoids
the dead ternary.
- Around line 400-406: The search <input> in apps-grid.tsx currently only uses a
placeholder and lacks an accessible name; add an aria-label (e.g.,
aria-label="Search apps") or associate it with a visible/visually-hidden <label>
so the input has an accessible name; update the input element where
value={query} and onChange={(e) => setQuery(e.target.value)} to include the
aria-label attribute (or label id via aria-labelledby) to satisfy WCAG.
- Around line 77-117: The AppLogo component uses the React namespace in its prop
type (React.FC<React.SVGProps<SVGSVGElement>>) but React isn't imported; add an
isolated type import like `import type React from "react";` alongside the
existing imports (e.g., the same import line that brings in useMemo/useState) so
the Icon prop and SVG prop types resolve for the AppLogo function and related
JSX.

In `@apps/ui/src/components/apps/devpass-upsell.tsx`:
- Around line 16-24: The file uses the React namespace in the APP_LOGOS type
declaration (React.FC<React.SVGProps<SVGSVGElement>>) but never imports React;
add a type-only import (import type React from "react") at the top of the file
so the TS compiler/IDE can resolve React for the APP_LOGOS declaration and
similar type references.

---

Nitpick comments:
In `@apps/ui/src/app/apps/page.tsx`:
- Around line 39-50: formatBigNumber in page.tsx is duplicated by formatTokens
in apps-grid.tsx; extract the shared logic into a single exported utility (e.g.,
export function formatTokens / formatBigNumber in a new shared module) that
defines the common numberFormatter and the B/M/K rules, then replace both local
implementations by importing and calling that shared function from page.tsx and
apps-grid.tsx (update references to formatBigNumber or formatTokens to the
unified export).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 48c5142e-8ba5-4c5b-b039-a5691a10703e

📥 Commits

Reviewing files that changed from the base of the PR and between 4f43710 and e9396fd.

⛔ Files ignored due to path filters (1)
  • apps/ui/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
📒 Files selected for processing (7)
  • apps/api/src/index.ts
  • apps/api/src/routes/public-apps.ts
  • apps/ui/src/app/apps/page.tsx
  • apps/ui/src/components/apps/app-metadata.ts
  • apps/ui/src/components/apps/apps-grid.tsx
  • apps/ui/src/components/apps/devpass-upsell.tsx
  • packages/db/src/seed.ts

Comment thread apps/api/src/routes/public-apps.ts
Comment thread apps/ui/src/app/apps/page.tsx
Comment thread apps/ui/src/components/apps/apps-grid.tsx
Comment thread apps/ui/src/components/apps/apps-grid.tsx
Comment thread apps/ui/src/components/apps/apps-grid.tsx
Comment thread apps/ui/src/components/apps/devpass-upsell.tsx

@steebchen-bot steebchen-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

won't work in prod

smakosh and others added 6 commits May 7, 2026 13:04
Switch /public/apps endpoint from raw log scan to the aggregated
globalSourceStats table. Filter out the literal 'unknown' source
(NULL log.source rows that the aggregator buckets together) so the
/apps page only lists named apps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@smakosh smakosh self-assigned this May 8, 2026
@smakosh
smakosh requested a review from steebchen-bot May 8, 2026 16:16
@smakosh
smakosh enabled auto-merge May 8, 2026 16:16
@smakosh
smakosh disabled auto-merge May 8, 2026 16:20
@smakosh
smakosh merged commit 7dd840d into main May 8, 2026
12 checks passed
@smakosh
smakosh deleted the feat/apps-page branch May 8, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants