feat: separate Import page for Tangled repos, fix source filtering + handles - Add /import page in navbar for browsing Tangled repos with Import buttons - Remove source tabs (All/Cospan/Tangled) from landing page — Explore shows all repos - Fix source filtering: page server load now passes source param to API - Fix handle resolution on repo detail page breadcrumb - Landing page focuses on Cospan repos, Tangled import is a dedicated flow

Author: Aaron Steven White
Commit 99b13f7ab765bac7ba6a0081ba256134caddc87c
Parent: 3fe5211805
Structural diff unavailable

These commits were pushed via plain git push, so no pre-parsed schemas are available. Install git-remote-cospan and re-push via panproto:// to see scope-level changes, breaking change detection, and semantic diffs.

brew install panproto/tap/git-remote-cospan
6 files changed +134 -38
@@ -8,6 +8,7 @@
88 
99 	const navLinks = [
1010 		{ href: '/', label: 'Explore' },
11+		{ href: '/import', label: 'Import' },
1112 		{ href: '/feed', label: 'Feed' },
1213 		{ href: '/search', label: 'Search' },
1314 	] as const;
@@ -3,15 +3,16 @@ import { listRepos } from '$lib/api/repo.js';
33 
44 export const load: PageServerLoad = async ({ url }) => {
55 	const protocol = url.searchParams.get('protocol') ?? undefined;
6+	const source = url.searchParams.get('source') ?? undefined;
67 
78 	try {
89 		const [trending, recent] = await Promise.all([
9-			listRepos({ limit: 12, sort: 'stars' as any, protocol } as any),
10-			listRepos({ limit: 12, sort: 'updated' as any, protocol } as any),
10+			listRepos({ limit: 12, source, protocol } as any),
11+			listRepos({ limit: 12, source, protocol } as any),
1112 		]);
12-		return { trending, recent, protocol: protocol ?? null };
13+		return { trending, recent, protocol: protocol ?? null, source: source ?? null };
1314 	} catch {
1415 		const empty = { items: [], cursor: null };
15-		return { trending: empty, recent: empty, protocol: protocol ?? null };
16+		return { trending: empty, recent: empty, protocol: protocol ?? null, source: source ?? null };
1617 	}
1718 };
@@ -8,7 +8,6 @@
88 
99 	let searchQuery = $state('');
1010 	let showDropdown = $state(false);
11-	let activeSource = $derived($page.url.searchParams.get('source') ?? 'all');
1211 
1312 	// Multi-select protocols from URL
1413 	let activeProtocols = $derived<string[]>(() => {
@@ -49,19 +48,6 @@
4948 		goto(qs ? `/?${qs}` : '/', { noScroll: true, replaceState: true });
5049 	}
5150 
52-	const sourceTabs = [
53-		{ value: 'all', label: 'All' },
54-		{ value: 'cospan', label: 'Cospan' },
55-		{ value: 'tangled', label: 'Tangled' },
56-	] as const;
57-
58-	function setSource(source: string) {
59-		const params = new URLSearchParams($page.url.searchParams);
60-		if (source === 'all') params.delete('source');
61-		else params.set('source', source);
62-		const qs = params.toString();
63-		goto(qs ? `/?${qs}` : '/', { noScroll: true, replaceState: true });
64-	}
6551 
6652 	function langColor(value: string): string {
6753 		let hash = 0;
@@ -128,24 +114,6 @@
128114 <section>
129115 	<div class="flex flex-wrap items-center justify-between gap-4 border-b border-line/40 py-4">
130116 		<div class="flex items-center gap-3">
131-			<!-- Source tabs -->
132-			<div class="flex items-center gap-0.5 rounded-lg bg-surface p-1">
133-				{#each sourceTabs as tab}
134-					<button
135-						type="button"
136-						onclick={() => setSource(tab.value)}
137-						class="rounded-md px-3.5 py-1.5 text-[12px] font-medium transition-all duration-150
138-							{activeSource === tab.value
139-								? 'bg-raised text-ink shadow-sm'
140-								: 'text-ghost hover:text-caption'}"
141-					>
142-						{tab.label}
143-					</button>
144-				{/each}
145-			</div>
146-
147-			<div class="h-5 w-px bg-line/60"></div>
148-
149117 			<!-- View tabs: Trending / Recent -->
150118 			<div class="flex items-center gap-0.5 rounded-lg bg-surface p-1">
151119 				<button
@@ -1,4 +1,5 @@
11 <script lang="ts">
2+	import { onMount } from 'svelte';
23 	import CommitList from '$lib/components/repo/CommitList.svelte';
34 	import RepoTabBar from '$lib/components/repo/RepoTabBar.svelte';
45 	import Breadcrumb from '$lib/components/shared/Breadcrumb.svelte';
@@ -6,22 +7,31 @@
67 	import StarButton from '$lib/components/shared/StarButton.svelte';
78 	import ForkButton from '$lib/components/shared/ForkButton.svelte';
89 	import { getAuth } from '$lib/stores/auth.svelte';
10+	import { resolveHandle } from '$lib/api/handle.js';
911 	import type { RefUpdate } from '$lib/api/ref-update.js';
1012 
1113 	let { data } = $props();
1214 
1315 	let auth = $derived(getAuth());
1416 	let isOwner = $derived(auth.authenticated && data.repo?.did === auth.did);
17+	let handle = $state('');
18+
19+	onMount(async () => {
20+		const did = data.repo?.did ?? data.did;
21+		handle = await resolveHandle(did);
22+	});
23+
24+	let ownerLabel = $derived(handle || data.repo?.did || data.did);
1525 
1626 	let basePath = $derived(data.repo ? `/${data.repo.did}/${data.repo.name}` : `/${data.did}/${data.repoName}`);
1727 
1828 	let crumbs = $derived(data.repo
1929 		? [
20-				{ label: data.repo.did, href: `/${data.repo.did}` },
30+				{ label: ownerLabel, href: `/${data.repo.did}` },
2131 				{ label: data.repo.name }
2232 			]
2333 		: [
24-				{ label: data.did, href: `/${data.did}` },
34+				{ label: ownerLabel, href: `/${data.did}` },
2535 				{ label: data.repoName }
2636 			]
2737 	);
@@ -0,0 +1,13 @@
1+import type { PageServerLoad } from './$types';
2+import { listRepos } from '$lib/api/repo.js';
3+
4+export const load: PageServerLoad = async ({ url }) => {
5+	const protocol = url.searchParams.get('protocol') ?? undefined;
6+
7+	try {
8+		const repos = await listRepos({ limit: 50, source: 'tangled', protocol } as any);
9+		return { repos, protocol: protocol ?? null };
10+	} catch {
11+		return { repos: { items: [], cursor: null }, protocol: protocol ?? null };
12+	}
13+};
cospan · schematic version control on atproto built on AT Protocol