A project tracker for decentralized social media platforms, clients, and tools

feat: improve UI and navigation enhancements

- Make project card headers clickable to navigate to homepage
- Replace GitHub icon with code icon in top bar, linking to tangled.sh
- Use code icon for non-GitHub repository links in project cards
- Update page title from "Vite + React + TS" to "Social Meshes"
- Replace Vite favicon with custom lightning bolt icon
- Set default sort to "Most Stars" instead of "Relevance"
- Remove unused "Relevance" and "Recently Updated" sort options

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+2 -2
index.html
···
<html lang="en">
<head>
<meta charset="UTF-8" />
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
+
<link rel="icon" type="image/svg+xml" href="/lightning.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
-
<title>Vite + React + TS</title>
+
<title>Social Meshes</title>
</head>
<body>
<div id="root"></div>
+3
public/lightning.svg
···
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#3b82f6">
+
<path d="M13 10V3L4 14h7v7l9-11h-7z" fill="#3b82f6" stroke="#3b82f6" stroke-width="1" stroke-linejoin="round" stroke-linecap="round"/>
+
</svg>
+1 -1
src/App.tsx
···
network: 'atproto',
query: '',
tags: [],
-
sort: 'relevance'
+
sort: 'stars'
});
useEffect(() => {
-2
src/components/FilterToolbar.tsx
···
}
const sortOptions: { value: FilterState['sort']; label: string }[] = [
-
{ value: 'relevance', label: 'Relevance' },
-
{ value: 'updated', label: 'Recently Updated' },
{ value: 'stars', label: 'Most Stars' },
{ value: 'alphabetical', label: 'Alphabetical' }
];
+23 -7
src/components/ProjectCard.tsx
···
</svg>
);
case 'repo':
-
return (
-
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
-
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
-
</svg>
-
);
+
// Use GitHub icon only for GitHub URLs, otherwise use code icon
+
if (url?.includes('github.com')) {
+
return (
+
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
+
</svg>
+
);
+
} else {
+
return (
+
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
+
</svg>
+
);
+
}
case 'docs':
return (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
···
activitypub: 'bg-purple-900 text-purple-200'
};
+
const homepageLink = project.links?.find(link => link.kind === 'homepage')?.url;
+
return (
<div className="bg-gray-800 rounded-lg overflow-hidden border border-gray-700 hover:border-gray-600 transition-colors h-full flex flex-col">
<div className="p-4 flex flex-col flex-grow space-y-3">
<div className="flex items-start justify-between">
-
<div className="flex items-center space-x-3">
+
<a
+
href={homepageLink}
+
target="_blank"
+
rel="noopener noreferrer"
+
className="flex items-center space-x-3 hover:opacity-80 transition-opacity cursor-pointer"
+
>
{project.logoUrl ? (
<img
src={project.logoUrl}
···
</p>
)}
</div>
-
</div>
+
</a>
{project.type === 'semi-platform' && (
<div className="relative group">
<svg
+4 -3
src/components/TopBar.tsx
···
</div>
<a
-
href="https://github.com/socialmeshes"
+
href="https://tangled.sh/@bretton.dev/socialmeshes-app"
target="_blank"
rel="noopener noreferrer"
className="text-gray-400 hover:text-white transition-colors"
+
title="View source code"
>
-
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
-
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
+
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
</svg>
</a>
</div>
+1 -1
src/types/project.ts
···
network: Network;
query: string;
tags: string[];
-
sort: "relevance" | "updated" | "stars" | "alphabetical";
+
sort: "stars" | "alphabetical";
};
+2 -10
src/utils/projectUtils.ts
···
}
switch (filters.sort) {
-
case 'updated':
-
return [...filtered].sort((a, b) => {
-
const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
-
const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
-
return dateB - dateA;
-
});
-
case 'stars':
return [...filtered].sort((a, b) => (b.stars || 0) - (a.stars || 0));
-
+
case 'alphabetical':
return [...filtered].sort((a, b) => a.name.localeCompare(b.name));
-
-
case 'relevance':
+
default:
return filtered;
}