diff --git a/docs/package.json b/docs/package.json index 58c7e5573..9e5aaf280 100644 --- a/docs/package.json +++ b/docs/package.json @@ -20,7 +20,8 @@ "generate:catalog": "tsx scripts/generate-example-catalog.ts", "generate:stackblitz": "tsx scripts/build-stackblitz-files.ts", "generate:releases": "tsx scripts/generate-releases.ts", - "generate:all": "pnpm generate:api && pnpm generate:screenshots && pnpm generate:catalog && pnpm generate:stackblitz" + "generate:showcase": "tsx scripts/generate-showcase-screenshots.ts", + "generate:all": "pnpm generate:api && pnpm generate:screenshots && pnpm generate:catalog && pnpm generate:stackblitz && pnpm generate:showcase" }, "devDependencies": { "@content-collections/core": "^0.14.0", diff --git a/docs/scripts/generate-showcase-screenshots.ts b/docs/scripts/generate-showcase-screenshots.ts new file mode 100644 index 000000000..bcb7bc4b0 --- /dev/null +++ b/docs/scripts/generate-showcase-screenshots.ts @@ -0,0 +1,101 @@ +/** + * Showcase Screenshot Generator + * + * Generates WebP screenshots for featured and supporter sites + * listed in the showcase page, named by slug (e.g. tenzir.webp). + * + * Sites with noregenerate: true are only captured if no image exists yet. + * + * @usage + * pnpm tsx scripts/generate-showcase-screenshots.ts + */ + +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { chromium } from 'playwright'; +import sharp from 'sharp'; +import { featuredSites, sponsorSites } from '../src/routes/docs/showcase/showcase.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const OUTPUT_DIR = path.resolve(__dirname, '../static/showcase'); + +const VIEWPORT = { width: 800, height: 1600 }; + +function toSlug(name: string): string { + return name.toLowerCase().replace(/[^a-z0-9]/g, ''); +} + +async function main() { + console.log('Showcase Screenshot Generator'); + console.log('==============================\n'); + + if (!fs.existsSync(OUTPUT_DIR)) { + fs.mkdirSync(OUTPUT_DIR, { recursive: true }); + } + + const sites = [...featuredSites, ...sponsorSites]; + + const browser = await chromium.launch(); + const context = await browser.newContext({ viewport: VIEWPORT }); + const page = await context.newPage(); + page.setDefaultTimeout(30000); + + let generated = 0; + let skipped = 0; + let errors = 0; + + for (const site of sites) { + const name = site.name ?? site.reponame ?? ''; + const slug = toSlug(name); + const outputPath = path.join(OUTPUT_DIR, `${slug}.webp`); + + if (site.noregenerate && fs.existsSync(outputPath)) { + console.log(`⊘ Skipped (noregenerate): ${slug}.webp`); + skipped++; + continue; + } + + try { + const captureUrl = site.showcaseurl ?? site.homepageurl; + if (!captureUrl) { + console.log(`⊘ Skipped (no homepage): ${slug}.webp`); + skipped++; + continue; + } + console.log(`Capturing ${name} → ${slug}.webp`); + try { + await page.goto(captureUrl, { waitUntil: 'networkidle' }); + } catch { + // Fallback for sites that never reach networkidle + await page.goto(captureUrl, { waitUntil: 'domcontentloaded' }); + await page.waitForTimeout(3000); + } + + const pngBuffer = await page.screenshot({ fullPage: false }); + + await sharp(pngBuffer).webp({ quality: 80 }).toFile(outputPath); + + console.log(` ✓ Saved ${slug}.webp`); + generated++; + } catch (error) { + console.error(` ✗ Failed ${name}:`, error instanceof Error ? error.message : error); + errors++; + } + } + + await browser.close(); + + console.log('\n=============================='); + console.log(`Generated: ${generated}`); + console.log(`Skipped: ${skipped}`); + console.log(`Errors: ${errors}`); + console.log(`\nImages saved to ${OUTPUT_DIR}`); +} + +main().catch((error) => { + console.error('Fatal error:', error); + process.exit(1); +}); diff --git a/docs/src/lib/contribspons.remote.ts b/docs/src/lib/contribspons.remote.ts new file mode 100644 index 000000000..4e746ecbb --- /dev/null +++ b/docs/src/lib/contribspons.remote.ts @@ -0,0 +1,198 @@ +import { query, getRequestEvent } from '$app/server'; +import { env } from '$env/dynamic/private'; +import { api, graphql } from './api'; +import { SPONSOR_OVERRIDES, TIER_LEVELS } from '../routes/docs/showcase/dependents-overrides'; + +type Person = { + username: string; + avatar: string; + link?: string; + contributions?: number; + monthlyAmount?: number; +}; +type SponsorTier = { sponsor: Person[]; level: string }; + +export const getContribSponsors = query(async () => { + const { fetch } = getRequestEvent(); + + const githubHeaders: Record = { + Accept: 'application/vnd.github.v3+json', + 'User-Agent': 'LayerChart docs' + }; + if (env.GITHUB_API_TOKEN) { + const prefix = env.GITHUB_API_TOKEN.startsWith('ghp_') ? 'token' : 'Bearer'; + githubHeaders['Authorization'] = `${prefix} ${env.GITHUB_API_TOKEN}`; + } + + const [contributors, sponsors] = await Promise.all([ + fetchContributors(fetch, githubHeaders), + fetchSponsors(fetch, githubHeaders) + ]); + + return { contributors, sponsors, TIER_LEVELS }; +}); + +async function fetchContributors( + fetch: typeof globalThis.fetch, + headers: Record +): Promise { + const contributors: Person[] = []; + let page = 1; + + while (true) { + const data = await api( + 'https://api.github.com', + 'repos/techniq/layerchart/contributors', + { fetch, headers, data: { per_page: '100', page: String(page) } } + ); + + if (!data || data.length === 0) break; + + for (const c of data) { + if (c.type === 'User') { + contributors.push({ + username: c.login, + avatar: c.avatar_url, + link: c.html_url, + contributions: c.contributions + }); + } + } + + if (data.length < 100) break; + page++; + } + + return contributors; +} + +async function fetchSponsors( + fetch: typeof globalThis.fetch, + headers: Record +): Promise { + if (!env.GITHUB_API_TOKEN) { + console.warn('No GITHUB_API_TOKEN set, skipping sponsors fetch'); + return []; + } + + type SponsorshipNode = { + sponsorEntity: { login: string; avatarUrl: string; url: string } | null; + tier: { name: string; monthlyPriceInDollars: number } | null; + isActive: boolean; + }; + type FlatSponsorNode = { login: string; avatarUrl: string; url: string } | null; + + const data = await graphql<{ + user: { + sponsorshipsAsMaintainer?: { nodes: SponsorshipNode[] }; + sponsors: { nodes: FlatSponsorNode[] }; + }; + }>( + 'https://api.github.com/graphql', + ` + query { + user(login: "techniq") { + sponsorshipsAsMaintainer(first: 100, activeOnly: false) { + nodes { + sponsorEntity { + ... on User { + login + avatarUrl + url + } + ... on Organization { + login + avatarUrl + url + } + } + tier { + name + monthlyPriceInDollars + } + isActive + } + } + sponsors(first: 100) { + nodes { + ... on User { + login + avatarUrl + url + } + ... on Organization { + login + avatarUrl + url + } + } + } + } + } + `, + {}, + { fetch, headers } + ); + + if (!data?.user) { + console.warn('Sponsors query returned no user data'); + return []; + } + + const tieredNodes = data.user.sponsorshipsAsMaintainer?.nodes; + if (tieredNodes && tieredNodes.length > 0) { + console.log( + 'Raw sponsors:', + tieredNodes.map((n) => ({ + login: n.sponsorEntity?.login, + tier: n.tier?.name, + price: n.tier?.monthlyPriceInDollars, + active: n.isActive + })) + ); + + const tierMap = new Map(); + for (const level of [...TIER_LEVELS.map((t) => t.level), 'Past Sponsors']) { + tierMap.set(level, []); + } + + for (const node of tieredNodes) { + if (!node.sponsorEntity) continue; + + const login = node.sponsorEntity.login; + const price = node.tier?.monthlyPriceInDollars ?? 0; + + const person: Person = { + username: login, + avatar: node.sponsorEntity.avatarUrl, + link: node.sponsorEntity.url, + monthlyAmount: price || undefined + }; + + if (!node.isActive) { + tierMap.get('Past Sponsors')!.push(person); + continue; + } + + const level = + SPONSOR_OVERRIDES[login.toLowerCase()]?.tierOverride ?? + TIER_LEVELS.find((t) => price >= t.min)?.level ?? + 'Backers Level'; + tierMap.get(level)!.push(person); + } + + return [...tierMap.entries()] + .filter(([, sponsors]) => sponsors.length > 0) + .map(([level, sponsor]) => ({ level, sponsor })); + } + + const flatNodes = data.user.sponsors?.nodes; + if (!flatNodes || flatNodes.length === 0) return []; + + const sponsors: Person[] = flatNodes + .filter((n): n is NonNullable => n != null && n.login != null) + .map((n) => ({ username: n.login, avatar: n.avatarUrl, link: n.url })); + + if (sponsors.length === 0) return []; + return [{ level: 'Backers Level', sponsor: sponsors }]; +} diff --git a/docs/src/routes/docs/showcase/+page.md b/docs/src/routes/docs/showcase/+page.md index 780d1303d..bfc509c8f 100644 --- a/docs/src/routes/docs/showcase/+page.md +++ b/docs/src/routes/docs/showcase/+page.md @@ -2,20 +2,20 @@ import Showcase from './Showcase.svelte'; import { getDependents } from './dependency.remote'; - const { featuredSites, supporterSites, popularSites, otherSites } = await getDependents(); + const { featuredSites, sponsorSites, popularSites, otherSites } = await getDependents(); # Showcase -## Supporters +## Sponsors - + -## Featured +[Become a sponsor](https://github.com/techniq/layerchart?tab=readme-ov-file#sponsors) - +## Featured -[Become a sponsor](https://github.com/techniq/layerchart?tab=readme-ov-file#sponsors) + ## Popular diff --git a/docs/src/routes/docs/showcase/Showcase.svelte b/docs/src/routes/docs/showcase/Showcase.svelte index bc8276d1e..76a2db257 100644 --- a/docs/src/routes/docs/showcase/Showcase.svelte +++ b/docs/src/routes/docs/showcase/Showcase.svelte @@ -1,48 +1,82 @@
{#each sites as site} -
- - {site.name ?? site.reponame} - - {#if site.description} -

{site.description}

+ {#if hero} + {@const slug = toSlug(site.name ?? site.reponame ?? '')} +
+ + + {site.name { + const img = e.currentTarget as HTMLImageElement; + img.onerror = null; + img.src = '/favicon.svg'; + img.classList.remove('object-cover', 'object-top'); + img.classList.add('object-contain', 'p-6'); + img.style.transform = 'none'; + }} + /> + + +
{/if} -
- {#if site.stars} - - - {site.stars.toLocaleString()} - - {/if} - {#if site.repourl} -
-
+ {/each} diff --git a/docs/src/routes/docs/showcase/dependency.remote.ts b/docs/src/routes/docs/showcase/dependency.remote.ts index ac2e07cd0..546095720 100644 --- a/docs/src/routes/docs/showcase/dependency.remote.ts +++ b/docs/src/routes/docs/showcase/dependency.remote.ts @@ -1,102 +1,49 @@ import { prerender } from '$app/server'; import { env } from '$env/dynamic/private'; - -const POPULAR_STAR_THRESHOLD = 100; -const OTHER_STAR_THRESHOLD = 10; +import { writeFileSync } from 'fs'; +import { fileURLToPath } from 'url'; +import { dirname, join } from 'path'; +import { FEATURED_SITES, HIGHLIGHTED_SITES, TIER_LEVELS } from './dependents-overrides'; +import { SPONSOR_OVERRIDES } from './dependents-overrides'; export type Dependent = { name?: string; reponame?: string; owner?: string; - description: string; + description?: string; repourl?: string; homepageurl?: string; + showcaseurl?: string; stars?: number; + noregenerate?: boolean; + tierOverride?: string; }; -export const getDependents = prerender(async () => { - const featuredSites: Dependent[] = [ - { - name: 'Zipline AI', - description: 'Features, context and embeddings for real-time AI/ML', - repourl: 'https://zipline.ai/', - homepageurl: 'https://github.com/zipline-ai' - }, - { - name: 'Github Analysis', - description: 'Analyze your GitHub repositories and NPM packages', - repourl: 'https://github.com/techniq/github-analysis', - homepageurl: 'https://github.techniq.dev' - }, - { - name: 'Strava Analysis', - description: 'Analyze your Strava activities', - repourl: 'https://github.com/techniq/strava-analysis', - homepageurl: 'https://strava.techniq.dev' - } - ]; +const POPULAR_STAR_THRESHOLD = 100; +const OTHER_STAR_THRESHOLD = 10; - const supporterSites: Dependent[] = [ - // could this be automated? pull sponsers, check dependents by owner === sponser, and add them here? - { - name: 'Tenzir', - description: 'Open source data pipelines for security teams', - repourl: 'https://github.com/tenzir', - homepageurl: 'https://tenzir.com/' - }, - { - name: 'shadcn-svelte', - description: 'shadcn/ui, but for Svelte.', - repourl: 'https://github.com/huntabyte/shadcn-svelte', - homepageurl: 'https://shadcn-svelte.com/' - }, - { - name: 'Sky Zoo', - description: 'Bluesky stats', - repourl: 'https://skyzoo.blue/', - homepageurl: 'https://github.com/jycouet/jyc.dev' - } - ]; +function writeShowcaseData(data: Record) { + const __dirname = dirname(fileURLToPath(import.meta.url)); + const entries = Object.entries(data) + .map( + ([key, value]) => + `export const ${key}: Dependent[] = ${JSON.stringify(value.filter((d) => d.description && d.homepageurl), null, '\t')};` + ) + .join('\n\n'); + writeFileSync( + join(__dirname, 'showcase.ts'), + `// Auto-generated by dependency.remote.ts — do not edit manually\n// Facilitates viewing of data, and used for showcase hero image generation.\n\n` + + `import type { Dependent } from './dependency.remote';\n\n` + + entries + + '\n' + ); + console.log(`[getDependents] Wrote showcase.ts`); +} - // These do not have a GH repo, but will be promoted by adding to the top of popular sites. - const highlightedSites: Dependent[] = [ - { - name: 'GEO audit', - description: 'GEO / AI audit that tracks your visibility impact', - homepageurl: 'https://www.geoaud.it/' - }, - { - name: 'RetireNumber', - description: 'Get a second opinion on your retirement number.', - homepageurl: 'https://retirenumber.com/' - }, - { - name: 'PowerOutage.com', - description: 'Tracks, records, and aggregates power outage data across the World', - homepageurl: 'https://poweroutage.com/' - }, - { - name: 'IOM UN Migration: Ukraine Regional Response', - description: 'Needs, Intentions, and Border Crossings', - homepageurl: - 'https://dtm.iom.int/online-interactive-resources/ukraine-regional-response-dashboard/index.html' - }, - { - name: 'Loyola Chicago: Center for Criminal Justice', - description: 'The First Year of the Pretrial Fairness Act', - homepageurl: 'https://pfa-1yr.loyolaccj.org/' - }, - { - name: 'ftop', - description: 'Comperative performance metrics for Fortnite islands', - homepageurl: 'https://ftop.app/' - }, - { - name: 'Nocturne', - description: 'A next-generation platform for diabetes management', - homepageurl: 'https://nocturne.app/' - } - ]; +export const getDependents = prerender(async () => { + const featuredSites = [...FEATURED_SITES]; + + const highlightedSites = [...HIGHLIGHTED_SITES]; const githubHeaders: Record = { Accept: 'application/vnd.github.v3+json', @@ -188,9 +135,97 @@ export const getDependents = prerender(async () => { `[getDependents] Step 2 - GraphQL details: ${((performance.now() - step2Start) / 1000).toFixed(2)}s (${dependents.length} repos, ${batchPromises.length} batches)` ); - // Step 3: Fetch stars for manually-listed sites (featured, supporters, highlighted) + // Step 2.5: Fetch active sponsor logins+tiers and partition dependents into sponsorSites + const step25Start = performance.now(); + + // Map login (lowercase) → tier rank (lower = higher tier) + const sponsorTierRank = new Map(); + if (env.GITHUB_API_TOKEN) { + try { + const res = await fetch('https://api.github.com/graphql', { + method: 'POST', + headers: githubHeaders, + body: JSON.stringify({ + query: `{ user(login: "techniq") { sponsorshipsAsMaintainer(first: 100, activeOnly: true) { nodes { sponsorEntity { ... on User { login } ... on Organization { login } } tier { monthlyPriceInDollars } } } } }` + }) + }); + if (res.ok) { + const { data } = await res.json(); + const nodes = data?.user?.sponsorshipsAsMaintainer?.nodes ?? []; + for (const node of nodes) { + const login = node.sponsorEntity?.login; + if (!login) continue; + const price = node.tier?.monthlyPriceInDollars ?? 0; + const level = + SPONSOR_OVERRIDES[login.toLowerCase()]?.tierOverride ?? + TIER_LEVELS.find((t) => price >= t.min)?.level ?? + 'Backers Level'; + const rank = TIER_LEVELS.findIndex((t) => t.level === level); + sponsorTierRank.set(login.toLowerCase(), rank === -1 ? TIER_LEVELS.length : rank); + } + } + } catch (e) { + console.error('[getDependents] Failed to fetch sponsor logins:', e); + } + } + const sponsorLogins = new Set(sponsorTierRank.keys()); + + const tierRankOf = (d: Dependent) => + sponsorTierRank.get(d.owner?.toLowerCase() ?? '') ?? TIER_LEVELS.length; + + // Partition dependents: sponsor-owned repos become sponsorSites, rest continues as normal + // Merge SPONSOR_OVERRIDES as base, dependent data layers on top + const sponsorDependents = dependents + .filter((d) => d.owner && sponsorLogins.has(d.owner.toLowerCase())) + .map((d) => { + const override = SPONSOR_OVERRIDES[d.owner!.toLowerCase()] ?? {}; + const nonNull = Object.fromEntries(Object.entries(d).filter(([, v]) => v != null)); + return { ...override, ...nonNull } as Dependent; + }) + .sort((a, b) => tierRankOf(a) - tierRankOf(b) || (b.stars ?? 0) - (a.stars ?? 0)); + const nonSponsorDependents = dependents.filter( + (d) => !d.owner || !sponsorLogins.has(d.owner.toLowerCase()) + ); + + // For sponsors with no matching dependent repo, fetch their GitHub profile as fallback + const matchedLogins = new Set(sponsorDependents.map((d) => d.owner?.toLowerCase())); + const unmatchedSponsors = [...sponsorLogins].filter((login) => !matchedLogins.has(login)); + const fallbackSponsorSites: Dependent[] = ( + await Promise.all( + unmatchedSponsors.map(async (login): Promise => { + try { + const res = await fetch(`https://api.github.com/users/${login}`, { + headers: githubHeaders + }); + const profile = res.ok ? await res.json() : null; + if (!profile?.login) return null; + return { + ...SPONSOR_OVERRIDES[login], + name: profile.name || profile.login, + owner: profile.login, + description: (profile.bio || '') as string, + repourl: profile.html_url, + ...(profile.blog ? { homepageurl: profile.blog } : {}) + }; + } catch (e) { + console.error(`[getDependents] Failed to fetch profile for sponsor ${login}:`, e); + return null; + } + }) + ) + ).filter((d) => d !== null) as Dependent[]; + + const sponsorSites: Dependent[] = [...sponsorDependents, ...fallbackSponsorSites].sort( + (a, b) => tierRankOf(a) - tierRankOf(b) || (b.stars ?? 0) - (a.stars ?? 0) + ); + + console.log( + `[getDependents] Step 2.5 - Sponsors: ${((performance.now() - step25Start) / 1000).toFixed(2)}s (${sponsorSites.length} sponsor sites from ${[...sponsorLogins].join(', ')})` + ); + + // Step 3: Fetch stars for manually-listed sites (featured, highlighted) const step3Start = performance.now(); - const manualSites = [...featuredSites, ...supporterSites, ...highlightedSites]; + const manualSites = [...featuredSites, ...highlightedSites]; const githubRepoPattern = /github\.com\/([^/]+)\/([^/]+)/; const manualSitesWithRepos = manualSites .map((site) => { @@ -239,19 +274,22 @@ export const getDependents = prerender(async () => { manualSites.flatMap((s) => [s.repourl, s.homepageurl].filter(Boolean)) ); - const filteredDependents = dependents + const filteredDependents = nonSponsorDependents .filter((d) => !manualUrls.has(d.repourl) && !manualUrls.has(d.homepageurl)) .sort((a, b) => (b.stars ?? 0) - (a.stars ?? 0)); - const popularSites = filteredDependents.filter( - (d) => (d.stars ?? 0) >= POPULAR_STAR_THRESHOLD - ); + const popularSites = filteredDependents.filter((d) => (d.stars ?? 0) >= POPULAR_STAR_THRESHOLD); const otherSites = [ + ...highlightedSites, ...filteredDependents.filter( (d) => (d.stars ?? 0) >= OTHER_STAR_THRESHOLD && (d.stars ?? 0) < POPULAR_STAR_THRESHOLD - ), - ...highlightedSites + ) ]; - return { featuredSites, supporterSites, popularSites, otherSites }; + const result = { featuredSites, sponsorSites, popularSites, otherSites }; + + // write to showcase.ts file, this is used for the showcase hero image generation and easy review of data. + writeShowcaseData(result); + + return result; }); diff --git a/docs/src/routes/docs/showcase/dependents-overrides.ts b/docs/src/routes/docs/showcase/dependents-overrides.ts new file mode 100644 index 000000000..329c067c7 --- /dev/null +++ b/docs/src/routes/docs/showcase/dependents-overrides.ts @@ -0,0 +1,86 @@ +import type { Dependent } from './dependency.remote'; + +export const TIER_LEVELS = [ + { level: 'Gold Level', min: 100 }, + { level: 'Silver Level', min: 25 }, + { level: 'Bronze Level', min: 10 }, + { level: 'Backers Level', min: 1 } +] as const; + +// Sponsors may not be on github, or if they are they may not have public repos. +// This file is used to override or fill in missing dependent data. +// fields here serve as the base; fetched dependent data layer on top +// Tier overrides cn be used to control order to some degree. +export const SPONSOR_OVERRIDES: Record> = { + tenzir: { name: 'Tenzir', homepageurl: 'https://tenzir.com/', tierOverride: 'Silver Level' }, + huntabyte: { homepageurl: 'https://shadcn-svelte.com/', tierOverride: 'Bronze Level' }, + jycouet: { + name: 'Sky Zoo', + homepageurl: 'https://skyzoo.blue/', + description: 'Bluesky Stats', + tierOverride: 'Bronze Level' + } +}; + +export const FEATURED_SITES: Dependent[] = [ + { + name: 'Zipline AI', + description: 'Features, context and embeddings for real-time AI/ML', + repourl: 'https://github.com/zipline-ai', + homepageurl: 'https://zipline.ai/' + }, + { + name: 'Github Analysis', + description: 'Analyze your GitHub repositories and NPM packages', + repourl: 'https://github.com/techniq/github-analysis', + homepageurl: 'https://github.techniq.dev', + noregenerate: true + }, + { + name: 'Strava Analysis', + description: 'Analyze your Strava activities', + repourl: 'https://github.com/techniq/strava-analysis', + homepageurl: 'https://strava.techniq.dev', + noregenerate: true + } +]; + +// These do not have a GH repo, but will be promoted by adding to the top of popular sites. +export const HIGHLIGHTED_SITES: Dependent[] = [ + { + name: 'GEO audit', + description: 'GEO / AI audit that tracks your visibility impact', + homepageurl: 'https://www.geoaud.it/' + }, + { + name: 'RetireNumber', + description: 'Get a second opinion on your retirement number.', + homepageurl: 'https://retirenumber.com/' + }, + { + name: 'PowerOutage.com', + description: 'Tracks, records, and aggregates power outage data across the World', + homepageurl: 'https://poweroutage.com/' + }, + { + name: 'IOM UN Migration: Ukraine Regional Response', + description: 'Needs, Intentions, and Border Crossings', + homepageurl: + 'https://dtm.iom.int/online-interactive-resources/ukraine-regional-response-dashboard/index.html' + }, + { + name: 'Loyola Chicago: Center for Criminal Justice', + description: 'The First Year of the Pretrial Fairness Act', + homepageurl: 'https://pfa-1yr.loyolaccj.org/' + }, + { + name: 'ftop', + description: 'Comperative performance metrics for Fortnite islands', + homepageurl: 'https://ftop.app/' + }, + { + name: 'Nocturne', + description: 'A next-generation platform for diabetes management', + homepageurl: 'https://nocturne.app/' + } +]; diff --git a/docs/src/routes/docs/showcase/showcase.ts b/docs/src/routes/docs/showcase/showcase.ts new file mode 100644 index 000000000..8bf747227 --- /dev/null +++ b/docs/src/routes/docs/showcase/showcase.ts @@ -0,0 +1,340 @@ +// Auto-generated by dependency.remote.ts — do not edit manually +// Facilitates viewing of data, and used for showcase hero image generation. + +import type { Dependent } from './dependency.remote'; + +export const featuredSites: Dependent[] = [ + { + "name": "Zipline AI", + "description": "Features, context and embeddings for real-time AI/ML", + "repourl": "https://github.com/zipline-ai", + "homepageurl": "https://zipline.ai/" + }, + { + "name": "Github Analysis", + "description": "Analyze your GitHub repositories and NPM packages", + "repourl": "https://github.com/techniq/github-analysis", + "homepageurl": "https://github.techniq.dev", + "noregenerate": true, + "stars": 6 + }, + { + "name": "Strava Analysis", + "description": "Analyze your Strava activities", + "repourl": "https://github.com/techniq/strava-analysis", + "homepageurl": "https://strava.techniq.dev", + "noregenerate": true, + "stars": 13 + } +]; + +export const sponsorSites: Dependent[] = [ + { + "name": "Tenzir", + "homepageurl": "https://tenzir.com", + "tierOverride": "Silver Level", + "owner": "tenzir", + "description": "Open source data pipelines for security teams", + "repourl": "https://github.com/tenzir" + }, + { + "homepageurl": "https://shadcn-svelte.com", + "tierOverride": "Bronze Level", + "owner": "huntabyte", + "reponame": "shadcn-svelte", + "description": "shadcn/ui, but for Svelte. ✨", + "repourl": "https://github.com/huntabyte/shadcn-svelte", + "stars": 8454 + }, + { + "name": "Sky Zoo", + "homepageurl": "https://skyzoo.blue/", + "description": "Bluesky Stats", + "tierOverride": "Bronze Level", + "owner": "jycouet", + "reponame": "jyc.dev", + "repourl": "https://github.com/jycouet/jyc.dev", + "stars": 24 + } +]; + +export const popularSites: Dependent[] = [ + { + "owner": "rajnandan1", + "reponame": "kener", + "description": "Stunning status pages, batteries included!", + "repourl": "https://github.com/rajnandan1/kener", + "homepageurl": "https://kener.ing/", + "stars": 4799 + }, + { + "owner": "exceptionless", + "reponame": "Exceptionless", + "description": "Exceptionless application", + "repourl": "https://github.com/exceptionless/Exceptionless", + "homepageurl": "https://exceptionless.com", + "stars": 2453 + }, + { + "owner": "amtoaer", + "reponame": "bili-sync", + "description": "由 Rust & Tokio 驱动的哔哩哔哩同步工具", + "repourl": "https://github.com/amtoaer/bili-sync", + "homepageurl": "https://bili-sync.amto.cc", + "stars": 2307 + }, + { + "owner": "LogicLabs-OU", + "reponame": "OpenArchiver", + "description": "An open-source platform for legally compliant email archiving.", + "repourl": "https://github.com/LogicLabs-OU/OpenArchiver", + "homepageurl": "https://openarchiver.com", + "stars": 1800 + }, + { + "owner": "javedh-dev", + "reponame": "tracktor", + "description": "Vehicle Tracking Management System", + "repourl": "https://github.com/javedh-dev/tracktor", + "homepageurl": "https://tracktor.bytedge.in", + "stars": 881 + }, + { + "owner": "pocketpaw", + "reponame": "pocketpaw", + "description": "Your AI agent in 30 seconds. Not 30 hours. Self-hosted, open-source personal AI with desktop installer, multi-agent Command Center(Deep Work), and 7-layer security. Anthropic, OpenAI, or Ollama.", + "repourl": "https://github.com/pocketpaw/pocketpaw", + "homepageurl": "http://pocketpaw.xyz/", + "stars": 616 + }, + { + "owner": "urania-dev", + "reponame": "snapp", + "description": "Yet Another Url Shortner. A simple excercise to learn Svelte.", + "repourl": "https://github.com/urania-dev/snapp", + "homepageurl": "https://snapp.li", + "stars": 390 + }, + { + "owner": "nickheyer", + "reponame": "discopanel", + "description": "A lightweight modded minecraft server hosting suite and management web app.", + "repourl": "https://github.com/nickheyer/discopanel", + "homepageurl": "https://discopanel.app", + "stars": 200 + }, + { + "owner": "accretional", + "reponame": "statue", + "description": "Statue: Svelte + Markdown Static Site Generator, launch a website for your business, blog, portfolio, or product in seconds, with one-line setup", + "repourl": "https://github.com/accretional/statue", + "homepageurl": "https://statue.dev", + "stars": 168 + }, + { + "owner": "hackclub", + "reponame": "hackatime", + "description": "Free, open source, Wakatime-compatible coding time tracker", + "repourl": "https://github.com/hackclub/hackatime", + "homepageurl": "https://hackatime.hackclub.com", + "stars": 114 + }, + { + "owner": "joachimchauvet", + "reponame": "modernstack-saas", + "description": "A modern SaaS template/boilerplate built with SvelteKit, Convex, Better Auth, and shadcn-svelte. Includes subscriptions, auth, user & admin dashboard, user settings, and more.", + "repourl": "https://github.com/joachimchauvet/modernstack-saas", + "homepageurl": "https://modernstack-saas.com", + "stars": 113 + }, + { + "owner": "chithi-dev", + "reponame": "chithi", + "description": "The next generation encrypted file sharing project", + "repourl": "https://github.com/chithi-dev/chithi", + "homepageurl": "https://landing.chithi.dev", + "stars": 101 + } +]; + +export const otherSites: Dependent[] = [ + { + "name": "GEO audit", + "description": "GEO / AI audit that tracks your visibility impact", + "homepageurl": "https://www.geoaud.it/" + }, + { + "name": "RetireNumber", + "description": "Get a second opinion on your retirement number.", + "homepageurl": "https://retirenumber.com/" + }, + { + "name": "PowerOutage.com", + "description": "Tracks, records, and aggregates power outage data across the World", + "homepageurl": "https://poweroutage.com/" + }, + { + "name": "IOM UN Migration: Ukraine Regional Response", + "description": "Needs, Intentions, and Border Crossings", + "homepageurl": "https://dtm.iom.int/online-interactive-resources/ukraine-regional-response-dashboard/index.html" + }, + { + "name": "Loyola Chicago: Center for Criminal Justice", + "description": "The First Year of the Pretrial Fairness Act", + "homepageurl": "https://pfa-1yr.loyolaccj.org/" + }, + { + "name": "ftop", + "description": "Comperative performance metrics for Fortnite islands", + "homepageurl": "https://ftop.app/" + }, + { + "name": "Nocturne", + "description": "A next-generation platform for diabetes management", + "homepageurl": "https://nocturne.app/" + }, + { + "owner": "webstonehq", + "reponame": "seaquel", + "description": "A modern, open-source database client for desktop and web.", + "repourl": "https://github.com/webstonehq/seaquel", + "homepageurl": "https://seaquel.app", + "stars": 69 + }, + { + "owner": "mquandalle", + "reponame": "mesaidesvelo", + "description": "Découvrez les aides à l’achat d’un vélo", + "repourl": "https://github.com/mquandalle/mesaidesvelo", + "homepageurl": "https://mesaidesvelo.fr", + "stars": 62 + }, + { + "owner": "OpenAttribution", + "reponame": "open-attribution", + "description": "Open source MMP for ownership of your mobile ad data", + "repourl": "https://github.com/OpenAttribution/open-attribution", + "homepageurl": "https://openattribution.dev", + "stars": 60 + }, + { + "owner": "Blakintosh", + "reponame": "svelte-flexiboards", + "description": "The headless drag-and-drop toolkit for Svelte.", + "repourl": "https://github.com/Blakintosh/svelte-flexiboards", + "homepageurl": "https://www.flexiboards.dev", + "stars": 56 + }, + { + "owner": "likeon", + "reponame": "geometa", + "description": "learnablemeta.com", + "repourl": "https://github.com/likeon/geometa", + "homepageurl": "https://learnablemeta.com/", + "stars": 55 + }, + { + "owner": "cardano-miners", + "reponame": "fortuna", + "description": "All the \"greatness\" of proof of work, now in smart contract form", + "repourl": "https://github.com/cardano-miners/fortuna", + "homepageurl": "https://minefortuna.com", + "stars": 54 + }, + { + "owner": "appgoblin-dev", + "reponame": "appgoblin", + "description": "Free App Store Analytics by AppGoblin. Research Andorid and iOS SDKs, Ad Networks, Trackers and MMPs. Free ASO marketing tools and app history. ", + "repourl": "https://github.com/appgoblin-dev/appgoblin", + "homepageurl": "https://appgoblin.info", + "stars": 46 + }, + { + "owner": "industrial-optimization-group", + "reponame": "DESDEO", + "description": "An open source fullstack framework for interactive multiobjective optimization and decision-support..", + "repourl": "https://github.com/industrial-optimization-group/DESDEO", + "homepageurl": "https://desdeo.readthedocs.io/en/latest/", + "stars": 44 + }, + { + "owner": "Michael-Obele", + "reponame": "Svelte-MiniApps", + "description": "This project is a collection of small, single-purpose applications written in Svelte 5. Each mini-app tackles a specific task, offering a simple and user-friendly experience for users.", + "repourl": "https://github.com/Michael-Obele/Svelte-MiniApps", + "homepageurl": "https://svelte-apps.me/", + "stars": 42 + }, + { + "owner": "rivenmedia", + "reponame": "riven-frontend", + "description": "Source code for Riven's frontend", + "repourl": "https://github.com/rivenmedia/riven-frontend", + "homepageurl": "https://riven.tv", + "stars": 41 + }, + { + "owner": "ScholarXIV", + "reponame": "enkokilish_bench", + "description": "Amharic Riddle Benchmark for LLMs", + "repourl": "https://github.com/ScholarXIV/enkokilish_bench", + "homepageurl": "https://enkokilish-bench.vercel.app", + "stars": 25 + }, + { + "owner": "PurelyAnecdotal", + "reponame": "gradecompass", + "description": "An advanced grade calculator designed to interface with StudentVUE®.", + "repourl": "https://github.com/PurelyAnecdotal/gradecompass", + "homepageurl": "https://gradecompass.org", + "stars": 18 + }, + { + "owner": "lipu-linku", + "reponame": "lipu", + "description": "lipu Linku, a toki pona dictionary", + "repourl": "https://github.com/lipu-linku/lipu", + "homepageurl": "https://linku.la/", + "stars": 18 + }, + { + "owner": "thedivtagguy", + "reponame": "site", + "description": "Aman's home on the internet", + "repourl": "https://github.com/thedivtagguy/site", + "homepageurl": "https://aman.bh", + "stars": 14 + }, + { + "owner": "stickerdaniel", + "reponame": "saas-starter", + "description": "SaaS starter kit built for humans with agentic workflows. SvelteKit, Convex, BetterAuth and full-stack type safety. Scaffold admin panels and production backends with ease.", + "repourl": "https://github.com/stickerdaniel/saas-starter", + "homepageurl": "https://saas.daniel.sticker.name", + "stars": 14 + }, + { + "owner": "jpoehnelt", + "reponame": "blog", + "description": "Code for my personal website", + "repourl": "https://github.com/jpoehnelt/blog", + "homepageurl": "https://justin.poehnelt.com", + "stars": 13 + }, + { + "owner": "openstate", + "reponame": "jodal", + "description": "Superkrachten voor journalisten. Doorzoek meer dan 2 miljoen overheidsdocumenten op één plek.", + "repourl": "https://github.com/openstate/jodal", + "homepageurl": "https://bron.live", + "stars": 11 + }, + { + "owner": "imranfadhil", + "reponame": "quick_pp", + "description": "Python package for Petrophysical analysis.", + "repourl": "https://github.com/imranfadhil/quick_pp", + "homepageurl": "https://quick-pp.readthedocs.io/en/latest/index.html", + "stars": 10 + } +]; diff --git a/docs/static/showcase/githubanalysis.webp b/docs/static/showcase/githubanalysis.webp new file mode 100644 index 000000000..270c935d9 Binary files /dev/null and b/docs/static/showcase/githubanalysis.webp differ diff --git a/docs/static/showcase/shadcnsvelte.webp b/docs/static/showcase/shadcnsvelte.webp new file mode 100644 index 000000000..7b0722492 Binary files /dev/null and b/docs/static/showcase/shadcnsvelte.webp differ diff --git a/docs/static/showcase/skyzoo.webp b/docs/static/showcase/skyzoo.webp new file mode 100644 index 000000000..9f2bc8dc8 Binary files /dev/null and b/docs/static/showcase/skyzoo.webp differ diff --git a/docs/static/showcase/stravaanalysis.webp b/docs/static/showcase/stravaanalysis.webp new file mode 100644 index 000000000..320c61075 Binary files /dev/null and b/docs/static/showcase/stravaanalysis.webp differ diff --git a/docs/static/showcase/tenzir.webp b/docs/static/showcase/tenzir.webp new file mode 100644 index 000000000..ae980e4f5 Binary files /dev/null and b/docs/static/showcase/tenzir.webp differ diff --git a/docs/static/showcase/ziplineai.webp b/docs/static/showcase/ziplineai.webp new file mode 100644 index 000000000..6db77e568 Binary files /dev/null and b/docs/static/showcase/ziplineai.webp differ