Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 35 additions & 9 deletions agents/tasks/tanstack-com-task-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,27 @@
- Links: PRs, issues, routes, components

### How to use this file

- Update status/notes as tasks progress. Keep routes/components and data sources referenced so any agent can continue seamlessly.
- Prefer reusing existing components and content models referenced below.

---

## 1. Metrics & Market Leadership Signals

**Goal:** Visible proof of dominance and growth.

### Audit snapshot

- Homepage metrics: `OpenSourceStats` counters present on homepage (`src/routes/_libraries/index.tsx` uses `OpenSourceStats`). Partial.
- "Trusted By": Component exists as text marquee (`src/components/TrustedByMarquee.tsx`). Not on homepage yet; currently used on some library pages (e.g. `src/routes/_libraries/table.$version.index.tsx`). Partial.
- NPM stats: Extensive interactive page exists at `src/routes/stats/npm/index.tsx` with charts and comparisons. Done (separate page).
- Backend metrics: `convex/stats.ts` + `@erquhart/convex-oss-stats` provides GitHub/NPM org metrics; `OpenSourceStats.tsx` consumes `api.stats.getGithubOwner`, `api.stats.getNpmOrg`. Done for aggregate; per-library not yet surfaced.

### Tasks

- [ ] Implement “Trusted By” on homepage

- Status: Backlog
- Notes:
- Reuse `TrustedByMarquee` but upgrade to support logos + links + tooltip proof.
Expand All @@ -35,9 +40,10 @@
- Renders without CLS, loops smoothly, accessible (ARIA, alt text). Logos swap dark/light.
- All entries have a proof link; no unverified brands.
- Links: `src/components/TrustedByMarquee.tsx`, `src/routes/_libraries/index.tsx`.
- Owner:
- Owner:

- [ ] Add Real-Time Metrics Counters (per-library + org rollup)

- Status: Partial (org rollup live via `OpenSourceStats`)
- Notes:
- Extend counters to per-library pages using existing Convex endpoints or add repo-level endpoints via `convex-oss-stats` if needed.
Expand All @@ -55,32 +61,37 @@
- Notes:
- Route: `src/routes/state-of-tanstack.tsx`.
- Include growth charts (npm downloads: reuse `NpmStatsChart.tsx` or embed portions of `stats/npm`), GitHub stars, contributors, dependents (available via Convex aggregation already powering `OpenSourceStats`).
- Community stats: Discord members (needs server function), newsletter subscribers (manual or vendor API), X/Twitter followers (manual or API), repository contributors (Convex or GitHub GraphQL on server).
- Ecosystem counts: partners (derive from `src/utils/partners.tsx`), plugins/tools (manual list or content collection).
- CTA to GitHub org.
- Community stats: Discord members (needs server function), newsletter subscribers (manual or vendor API), X/Twitter followers (manual or API), repository contributors (Convex or GitHub GraphQL on server).
- Ecosystem counts: partners (derive from `src/utils/partners.tsx`), plugins/tools (manual list or content collection).
- CTA to GitHub org.
- Acceptance:
- Page loads instantly with cached metrics; charts are responsive and accessible.
- Sources and last-updated timestamps shown.
- Links: `src/components/NpmStatsChart.tsx`, `src/components/OpenSourceStats.tsx`, `src/routes/stats/npm/index.tsx`, `src/utils/partners.tsx`.
- Owner:

### Tech/context

- Data: `@erquhart/convex-oss-stats` via `convex/stats.ts` (org-level GitHub star/contributor/dependent counts, npm downloads). Consider adding per-repo endpoints if needed.
- Secrets: Configure any tokens via Netlify/Convex env; never expose client-side.
- Accessibility: Ensure counters/animations are readable and respect `prefers-reduced-motion`.

---

## 2. Founder & Team Story

**Goal:** Frame the team as visionary and credible.

### Audit snapshot

- Ethos page exists: `src/routes/_libraries/ethos.tsx` (narrative and positioning).
- Maintainers directory page exists: `src/routes/_libraries/maintainers.tsx` with `MaintainerCard` variants and filters; bios sourced from `src/libraries/maintainers`.
- No dedicated "About" route; no speaking engagements index; no curated endorsements/tweets.

### Tasks

- [ ] Redesign/Create “About” page

- Status: Backlog
- Notes:
- Route: `src/routes/about.tsx`.
Expand All @@ -90,6 +101,7 @@
- Links: `src/components/MaintainerCard.tsx`, `src/routes/_libraries/maintainers.tsx`.

- [ ] Speaking Engagements section

- Status: Backlog
- Notes:
- Add to About or standalone `src/routes/speaking.tsx`.
Expand All @@ -106,22 +118,25 @@
- Acceptance: Renders endorsements with attribution and embedded tweets with proper theming.

### Tech/context

- Reuse `MaintainerCard` and existing images in `src/images/`.
- Avoid fetching social embeds at build if rate-limited; hydrate on client or cache server-side.

---



## 4. Commercial Hooks

**Goal:** Show monetizable pathways.

### Audit snapshot

- Enterprise/Support: `src/routes/_libraries/paid-support.tsx` exists with HubSpot script and CTAs. Partial substitute for "Enterprise" page.
- No dedicated Partner Program page.

### Tasks

- [ ] “Enterprise” page

- Status: Partial
- Notes:
- Option 1: Rename and expand `paid-support` into `enterprise` (route alias + updated copy) while keeping legacy route.
Expand All @@ -137,18 +152,20 @@
- Link to Partners page.
- Acceptance: Published page with clear application CTA.



---

## 5. Future Vision Page

**Goal:** Show long-term upside.

### Audit snapshot

- No public roadmap found; ethos narrative exists but not a vision statement page.

### Tasks

- [ ] Public Roadmap page

- Status: Backlog
- Notes:
- Route: `src/routes/roadmap.tsx`.
Expand All @@ -167,13 +184,17 @@
---

## 6. Media & Momentum

**Goal:** Make hype and credibility easy to digest.

### Audit snapshot

- No dedicated media kit, in-the-news, or social proof feeds found.

### Tasks

- [ ] Press/Media Kit page

- Status: Backlog
- Notes:
- Route: `src/routes/media-kit.tsx`.
Expand All @@ -182,6 +203,7 @@
- Acceptance: Page provides direct downloads and usage rules.

- [ ] In the News page

- Status: Backlog
- Notes:
- Route: `src/routes/news.tsx`.
Expand All @@ -199,6 +221,7 @@
---

### Shared implementation notes

- Routing: New pages should be added under `src/routes/*` using TanStack Start conventions; update nav/footers as needed.
- Data placement: Prefer `src/data/*.ts` (typed) or `content/*.(json|yaml)` for editorial lists. Avoid hardcoding in components unless small.
- Theming: Provide dark/light logo variants; `public/` is ideal for static assets.
Expand All @@ -208,19 +231,22 @@
- Analytics: Add outbound link tracking if available (future).

### Potential blockers

- External API limits (GitHub GraphQL, Discord member count, X/Twitter API). Prefer server-side fetch with caching or public embed widgets.
- Legal/branding approvals for “Trusted By” logos—require proof links.

### Quick links to relevant code

- Homepage: `src/routes/_libraries/index.tsx`
- Metrics: `src/components/OpenSourceStats.tsx`, `convex/stats.ts`, `src/components/NpmStatsChart.tsx`, `src/routes/stats/npm/index.tsx`
- Trusted By: `src/components/TrustedByMarquee.tsx`
- Team/Ethos: `src/routes/_libraries/ethos.tsx`, `src/routes/_libraries/maintainers.tsx`, `src/components/MaintainerCard.tsx`
- SEO helper: `src/utils/seo`

### Ownership & tracking

- For each task above, fill in:
- Owner:
- Issue/PR links:
- Status:
- Next step:
- Next step:
4 changes: 2 additions & 2 deletions convex/auth.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export default {
providers: [
{
domain: process.env.CONVEX_SITE_URL,
applicationID: "convex",
applicationID: 'convex',
},
],
};
}
3 changes: 3 additions & 0 deletions src/components/MarkdownLink.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Link } from '@tanstack/react-router'
import type { HTMLProps } from 'react'
import { normalizeMarkdownPath } from '~/utils/normalize-markdown-path'

export function MarkdownLink({
href: hrefProp,
Expand All @@ -16,6 +17,8 @@ export function MarkdownLink({

const [hrefWithoutHash, hash] = hrefProp?.split('#') ?? []
let [to] = hrefWithoutHash?.split('.md') ?? []

to = normalizeMarkdownPath(to)

return (
<Link
Expand Down
103 changes: 103 additions & 0 deletions src/components/test-normalize-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Test suite for markdown link path normalization
// This can be run with: npx tsx src/components/test-normalize-path.ts

import { normalizeMarkdownPath } from '../utils/normalize-markdown-path'

// Test cases
const testCases: Array<[string, string, string]> = [
// [input, expected, description]

// Paths that should be modified
['./foo', '../foo', 'Same directory with ./'],
['./guides/foo', '../guides/foo', 'Subdirectory with ./'],
['./overview.md#api-reference', '../overview.md#api-reference', 'Same directory with ./ and hash'],
['./installation.md', '../installation.md', 'Real example from quick-start.md'],
['./overview.md', '../overview.md', 'Real example from quick-start.md'],
['./live-queries.md', '../live-queries.md', 'Real example from quick-start.md'],
['foo', '../foo', 'Bare filename (same directory)'],
['guides/foo', '../guides/foo', 'Bare subdirectory path'],
['guides/subfolder/foo', '../guides/subfolder/foo', 'Nested bare path'],
['live-queries.md', '../live-queries.md', 'Real bare filename from overview.md'],

// Paths that should NOT be modified (from real DB docs)
['../foo', '../foo', 'Already has ../'],
['../classes/foo', '../classes/foo', 'Already has ../ with subdirectory'],
['../classes/aggregatefunctionnotinselecterror.md', '../classes/aggregatefunctionnotinselecterror.md', 'Real example from reference docs'],
['../interfaces/btreeindexoptions.md', '../interfaces/btreeindexoptions.md', 'Real example from reference docs'],
['../type-aliases/changelistener.md', '../type-aliases/changelistener.md', 'Real example from reference docs'],
['../../foo', '../../foo', 'Multiple ../'],
['/absolute/path', '/absolute/path', 'Absolute path'],
['http://example.com', 'http://example.com', 'HTTP URL'],
['https://example.com', 'https://example.com', 'HTTPS URL'],
['https://github.com/TanStack/db/blob/main/packages/db/src/types.ts#L228', 'https://github.com/TanStack/db/blob/main/packages/db/src/types.ts#L228', 'GitHub source link'],

// Real examples from TanStack Router docs
['../learn-the-basics.md', '../learn-the-basics.md', 'Router: Parent directory link'],
['../hosting.md', '../hosting.md', 'Router: Parent directory hosting guide'],
['../server-functions.md', '../server-functions.md', 'Router: Parent directory server functions'],
['../server-routes.md', '../server-routes.md', 'Router: Parent directory server routes'],
['../middleware.md', '../middleware.md', 'Router: Parent directory middleware'],
]

// Run tests
console.log('Running path normalization tests:\n')
let passed = 0
let failed = 0

for (const [input, expected, description] of testCases) {
const result = normalizeMarkdownPath(input)
const isPass = result === expected

if (isPass) {
console.log(`✅ PASS: ${description}`)
console.log(` Input: "${input}" → Output: "${result}"`)
passed++
} else {
console.log(`❌ FAIL: ${description}`)
console.log(` Input: "${input}"`)
console.log(` Expected: "${expected}"`)
console.log(` Got: "${result}"`)
failed++
}
console.log('')
}

console.log(`\nResults: ${passed} passed, ${failed} failed`)

// Edge cases to consider
console.log('\n--- Edge Cases to Consider ---')
console.log('1. Hash fragments: "foo#section" should become "../foo#section"')
console.log('2. Query params: "foo?param=value" should become "../foo?param=value"')
console.log('3. Special protocols: "mailto:", "javascript:", etc. should not be modified')
console.log('4. Empty string or undefined should return as-is')

// Test edge cases
console.log('\n--- Testing Edge Cases ---\n')

const edgeCases: Array<[string | undefined, string | undefined, string]> = [
['foo#section', '../foo#section', 'Hash fragment'],
['./foo#section', '../foo#section', 'With ./ and hash'],
['../foo#section', '../foo#section', 'Already ../ with hash'],
[undefined, undefined, 'Undefined input'],
['', '', 'Empty string'],
['#section', '#section', 'Hash only (should not be modified)'],
['mailto:[email protected]', 'mailto:[email protected]', 'Mailto protocol'],
['javascript:void(0)', 'javascript:void(0)', 'Javascript protocol'],
]

for (const [input, expected, description] of edgeCases) {
const result = normalizeMarkdownPath(input)
const isPass = result === expected

if (isPass) {
console.log(`✅ PASS: ${description}`)
console.log(` Input: "${input}" → Output: "${result}"`)
} else {
console.log(`❌ FAIL: ${description}`)
console.log(` Input: "${input}"`)
console.log(` Expected: "${expected}"`)
console.log(` Got: "${result}"`)
}
console.log('')
}

2 changes: 1 addition & 1 deletion src/routes/_libraries/form.$version.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export default function FormVersionIndex() {
className: 'bg-yellow-400 text-black',
}}
/>

<div className="w-fit mx-auto px-4">
<OpenSourceStats library={library} />
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/_libraries/pacer.$version.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export default function PacerVersionIndex() {
<div className="w-fit mx-auto px-4">
<OpenSourceStats library={library} />
</div>

<LibraryFeatureHighlights
featureHighlights={library.featureHighlights}
/>
Expand Down
2 changes: 1 addition & 1 deletion src/styles/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -735,4 +735,4 @@ mark {
font-style: italic;
font-weight: 900;
src: url('/fonts/inter-v19-latin-900italic.woff2') format('woff2');
}
}
41 changes: 41 additions & 0 deletions src/utils/normalize-markdown-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Normalizes markdown link paths for TanStack website routing
*
* GitHub markdown links work differently from our router structure:
* - In GitHub: ./guides/foo.md works from docs/overview.md
* - In our router: we need ../guides/foo because the current path is /lib/version/docs/overview (not overview/)
*
* @param path The original markdown link path
* @returns The normalized path for website routing
*/
export function normalizeMarkdownPath(path: string | undefined): string | undefined {
if (!path) return path

// Don't modify:
// - Absolute paths (/)
// - Paths already using ../
// - External links (http/https)
// - Hash-only links (#)
// - Special protocols (mailto:, javascript:, etc)
if (
path.startsWith('/') ||
path.startsWith('../') ||
path.startsWith('http') ||
path.startsWith('#') ||
path.includes(':') // Catches mailto:, javascript:, etc
) {
return path
}

// Convert ./path to ../path (GitHub style to website style)
if (path.startsWith('./')) {
return '../' + path.slice(2)
}

// Handle bare paths (no ./ prefix)
// These are treated as siblings, so prepend ../
// This covers:
// - "foo" (same directory file)
// - "guides/foo" (subdirectory)
return '../' + path
}
Loading