Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .github/workflows/build-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ jobs:

- name: Build site
run: pnpm --filter docs build
env:
GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload build artifact
uses: actions/upload-artifact@v4
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:

- name: Build site
run: pnpm --filter docs build
env:
GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Deploy to Cloudflare Pages
uses: AdrianGonz97/refined-cf-pages-action@v1
Expand Down
47 changes: 20 additions & 27 deletions docs/src/routes/docs/showcase/+page.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,26 @@
<script lang="ts">
import { Button, Tooltip } from 'svelte-ux';
import Showcase from './Showcase.svelte';
import { getDependents } from './dependency.remote';

import LucideGithub from '~icons/lucide/github';
import LucideSquareArrowOutUpRight from '~icons/lucide/square-arrow-out-up-right'

import { sites } from './data';
const { featuredSites, supporterSites, popularSites, otherSites } = await getDependents();
</script>

# Showcase

<div class="grid grid-cols-sm gap-3">
{#each sites as site}
<div class="flex flex-col border border-primary/20 rounded-lg px-3 py-2 bg-linear-to-b from-primary/8 to-primary/2 backdrop-blur">
<a href={site.url ?? site.source} target="_blank" class="text-lg font-medium">
{site.name}
</a>
{#if site.description}
<p class="text-sm text-surface-content/50">{site.description}</p>
{/if}
<div class="grow flex items-end justify-end gap-1">
{#if site.source}
<Button href={site.source} target="_blank" icon={LucideGithub} class="size-7 text-surface-content/50 hover:text-surface-content" />
{/if}
{#if site.url}
<Button href={site.url} target="_blank" icon={LucideSquareArrowOutUpRight} class="size-7 text-surface-content/50 hover:text-surface-content" />
{/if}
</div>
</div>
{/each}
</div>

[More](https://github.com/techniq/layerchart/network/dependents)
## Featured

<Showcase sites={featuredSites} />

## [Supporters](https://github.com/techniq/layerchart?tab=readme-ov-file#sponsors)

<Showcase sites={supporterSites} />

## Popular

<Showcase sites={popularSites} />

## Other

<Showcase sites={otherSites} />

## [More](https://github.com/techniq/layerchart/network/dependents)
48 changes: 48 additions & 0 deletions docs/src/routes/docs/showcase/Showcase.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script lang="ts">
import { Button } from 'svelte-ux';
import LucideGithub from '~icons/lucide/github';
import LucideStar from '~icons/lucide/star';
import LucideSquareArrowOutUpRight from '~icons/lucide/square-arrow-out-up-right';
import type { Dependent } from './dependency.remote';

let { sites }: { sites: Dependent[] } = $props();
</script>

<div class="grid grid-cols-sm gap-3">
{#each sites as site}
<div
class="flex flex-col border border-primary/20 rounded-lg px-3 py-2 bg-linear-to-b from-primary/8 to-primary/2 backdrop-blur"
>
<a href={site.repourl ?? site.homepageurl} target="_blank" class="text-lg font-medium">
{site.name ?? site.reponame}
</a>
{#if site.description}
<p class="text-sm text-surface-content/50">{site.description}</p>
{/if}
<div class="grow flex items-end justify-end gap-1">
{#if site.stars}
<span class="flex items-center gap-1 text-sm text-surface-content/50 mr-auto">
<LucideStar class="size-4" />
{site.stars.toLocaleString()}
</span>
{/if}
{#if site.repourl}
<Button
href={site.repourl}
target="_blank"
icon={LucideGithub}
class="size-7 text-surface-content/50 hover:text-surface-content"
/>
{/if}
{#if site.homepageurl}
<Button
href={site.homepageurl}
target="_blank"
icon={LucideSquareArrowOutUpRight}
class="size-7 text-surface-content/50 hover:text-surface-content"
/>
{/if}
</div>
</div>
{/each}
</div>
206 changes: 206 additions & 0 deletions docs/src/routes/docs/showcase/dependency.remote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import { prerender } from '$app/server';
import { env } from '$env/dynamic/private';

const POPULAR_STAR_THRESHOLD = 100;
const OTHER_STAR_THRESHOLD = 10;

export type Dependent = {
name?: string;
reponame?: string;
owner?: string;
description: string;
repourl?: string;
homepageurl?: string;
stars?: number;
};

export const getDependents = prerender(async () => {

const featuredSites: Dependent[] = [
{
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'
},
{
name: 'Zipline AI',
description: 'Features, context and embeddings for real-time AI/ML',
repourl: 'https://zipline.ai/',
homepageurl: 'https://github.com/zipline-ai'
}
];

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'
}
];

// 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/'
}
];

const githubHeaders: Record<string, string> = {
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 totalStart = performance.now();

// Step 1: Find repos with "layerchart" in package.json via code search
// NOTE: Code search API has a strict rate limit, so pages are fetched sequentially
const repoSet = new Set<string>();
let page = 1;
const perPage = 100;

const step1Start = performance.now();
while (true) {
const searchUrl = `https://api.github.com/search/code?q=${encodeURIComponent('"layerchart" filename:package.json')}&per_page=${perPage}&page=${page}`;
const res = await fetch(searchUrl, { headers: githubHeaders });

if (!res.ok) {
console.error(`GitHub code search failed: ${res.status} ${res.statusText}`);
break;
}

const data = await res.json();
const items = data.items ?? [];

for (const item of items) {
const fullName = item.repository?.full_name;
if (fullName && fullName !== 'techniq/layerchart') {
repoSet.add(fullName);
}
}

if (items.length < perPage || repoSet.size >= data.total_count) break;
page++;
}
console.log(
`[getDependents] Step 1 - Code search: ${((performance.now() - step1Start) / 1000).toFixed(2)}s (${repoSet.size} repos found, ${page} pages)`
);

// Step 2: Batch-fetch repo details via GitHub GraphQL (parallel)
const step2Start = performance.now();
const repos = [...repoSet];
const batchSize = 50;

const batchPromises = [];
for (let i = 0; i < repos.length; i += batchSize) {
const batch = repos.slice(i, i + batchSize);
const fragments = batch
.map((fullName, idx) => {
const [owner, name] = fullName.split('/');
return `repo${idx}: repository(owner: ${JSON.stringify(owner)}, name: ${JSON.stringify(name)}) { stargazerCount description homepageUrl url owner { login } name }`;
})
.join('\n');

batchPromises.push(
fetch('https://api.github.com/graphql', {
method: 'POST',
headers: githubHeaders,
body: JSON.stringify({ query: `{ ${fragments} }` })
}).then(async (res) => {
if (!res.ok) return [];
const { data } = await res.json();
if (!data) return [];
return Object.values(data)
.filter(Boolean)
.map((repo: any) => ({
owner: repo.owner.login,
reponame: repo.name,
description: repo.description || null,
repourl: repo.url,
homepageurl: repo.homepageUrl || null,
stars: repo.stargazerCount
}));
})
);
}

const batchResults = await Promise.all(batchPromises);
const dependents: Dependent[] = batchResults.flat();

console.log(
`[getDependents] Step 2 - GraphQL details: ${((performance.now() - step2Start) / 1000).toFixed(2)}s (${dependents.length} repos, ${batchPromises.length} batches)`
);
console.log(`[getDependents] Total: ${((performance.now() - totalStart) / 1000).toFixed(2)}s`);

dependents
.sort((a, b) => (b.stars ?? 0) - (a.stars ?? 0)) // Sort by stars descending
.filter((d) => featuredSites.some((f) => f.reponame === d.reponame)) // Filter out any featured sites
.filter((d) => supporterSites.some((s) => s.reponame === d.reponame)); // Filter out any supporter sites
const popularSites = [
...highlightedSites,
...dependents.filter((d) => (d.stars ?? 0) >= POPULAR_STAR_THRESHOLD)
];
const otherSites = dependents.filter(
(d) => (d.stars ?? 0) >= OTHER_STAR_THRESHOLD && (d.stars ?? 0) < POPULAR_STAR_THRESHOLD
);

return { featuredSites, supporterSites, popularSites, otherSites };
});
Loading