diff --git a/agents/tasks/tanstack-com-task-list.md b/agents/tasks/tanstack-com-task-list.md index 4709bff3..3a9509a8 100644 --- a/agents/tasks/tanstack-com-task-list.md +++ b/agents/tasks/tanstack-com-task-list.md @@ -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. @@ -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. @@ -55,9 +61,9 @@ - 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. @@ -65,6 +71,7 @@ - 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`. @@ -72,15 +79,19 @@ --- ## 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`. @@ -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`. @@ -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. @@ -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`. @@ -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`. @@ -182,6 +203,7 @@ - Acceptance: Page provides direct downloads and usage rules. - [ ] In the News page + - Status: Backlog - Notes: - Route: `src/routes/news.tsx`. @@ -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. @@ -208,10 +231,12 @@ - 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` @@ -219,8 +244,9 @@ - SEO helper: `src/utils/seo` ### Ownership & tracking + - For each task above, fill in: - Owner: - Issue/PR links: - Status: - - Next step: \ No newline at end of file + - Next step: diff --git a/convex/auth.config.ts b/convex/auth.config.ts index f4eb5646..afc62641 100644 --- a/convex/auth.config.ts +++ b/convex/auth.config.ts @@ -2,7 +2,7 @@ export default { providers: [ { domain: process.env.CONVEX_SITE_URL, - applicationID: "convex", + applicationID: 'convex', }, ], -}; +} diff --git a/src/components/MarkdownLink.tsx b/src/components/MarkdownLink.tsx index 3fca7017..bc37e508 100644 --- a/src/components/MarkdownLink.tsx +++ b/src/components/MarkdownLink.tsx @@ -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, @@ -16,6 +17,8 @@ export function MarkdownLink({ const [hrefWithoutHash, hash] = hrefProp?.split('#') ?? [] let [to] = hrefWithoutHash?.split('.md') ?? [] + + to = normalizeMarkdownPath(to) return ( = [ + // [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:test@example.com', 'mailto:test@example.com', '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('') +} + diff --git a/src/routes/_libraries/form.$version.index.tsx b/src/routes/_libraries/form.$version.index.tsx index cc21f381..1da879df 100644 --- a/src/routes/_libraries/form.$version.index.tsx +++ b/src/routes/_libraries/form.$version.index.tsx @@ -53,7 +53,7 @@ export default function FormVersionIndex() { className: 'bg-yellow-400 text-black', }} /> - +
diff --git a/src/routes/_libraries/pacer.$version.index.tsx b/src/routes/_libraries/pacer.$version.index.tsx index 90a9f175..aa322e32 100644 --- a/src/routes/_libraries/pacer.$version.index.tsx +++ b/src/routes/_libraries/pacer.$version.index.tsx @@ -49,7 +49,7 @@ export default function PacerVersionIndex() {
- + diff --git a/src/styles/app.css b/src/styles/app.css index f10d1b11..fa7d305e 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -735,4 +735,4 @@ mark { font-style: italic; font-weight: 900; src: url('/fonts/inter-v19-latin-900italic.woff2') format('woff2'); -} \ No newline at end of file +} diff --git a/src/utils/normalize-markdown-path.ts b/src/utils/normalize-markdown-path.ts new file mode 100644 index 00000000..873eba3a --- /dev/null +++ b/src/utils/normalize-markdown-path.ts @@ -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 +} \ No newline at end of file