mirror of
https://github.com/aljazceru/awesome-nostr.git
synced 2025-02-22 14:48:59 +00:00
so far got the basics down, categories and lists are being populated for the most part, just need to go through and check them all and improve the visual to make it more pleasing
This commit is contained in:
parent
d597ac2e37
commit
a609d89adc
54
index.html
Normal file
54
index.html
Normal file
@ -0,0 +1,54 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Awesome Nostr Resources</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<!-- Sidebar Navigation -->
|
||||
<nav class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1>Nostr Resources</h1>
|
||||
<button id="menuToggle" class="menu-toggle">
|
||||
<i class="fas fa-bars"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="sidebar-content">
|
||||
<div class="search-box">
|
||||
<input type="text" id="search" placeholder="Search resources...">
|
||||
<i class="fas fa-search"></i>
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<!-- Categories will be dynamically populated here -->
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
<div class="content-header">
|
||||
<div class="view-controls">
|
||||
<button id="darkModeToggle" class="theme-toggle">
|
||||
<i class="fas fa-moon"></i>
|
||||
</button>
|
||||
<div class="sort-controls">
|
||||
<select id="sortSelect">
|
||||
<option value="stars">Sort by Stars</option>
|
||||
<option value="name">Sort by Name</option>
|
||||
<option value="recent">Sort by Recent</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Resource sections will be dynamically populated -->
|
||||
<div id="resources-container"></div>
|
||||
</main>
|
||||
</div>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
320
script.js
Normal file
320
script.js
Normal file
@ -0,0 +1,320 @@
|
||||
// Dark mode toggle
|
||||
const darkModeToggle = document.getElementById('darkModeToggle');
|
||||
const body = document.body;
|
||||
|
||||
darkModeToggle.addEventListener('click', () => {
|
||||
body.dataset.theme = body.dataset.theme === 'dark' ? 'light' : 'dark';
|
||||
darkModeToggle.innerHTML = body.dataset.theme === 'dark'
|
||||
? '<i class="fas fa-sun"></i>'
|
||||
: '<i class="fas fa-moon"></i>';
|
||||
});
|
||||
|
||||
// Mobile menu toggle
|
||||
const menuToggle = document.getElementById('menuToggle');
|
||||
const sidebar = document.querySelector('.sidebar');
|
||||
|
||||
menuToggle.addEventListener('click', () => {
|
||||
sidebar.classList.toggle('active');
|
||||
});
|
||||
|
||||
// Search functionality
|
||||
const searchInput = document.getElementById('search');
|
||||
const resourceCards = document.querySelectorAll('.resource-card');
|
||||
|
||||
searchInput.addEventListener('input', (e) => {
|
||||
const searchTerm = e.target.value.toLowerCase();
|
||||
resourceCards.forEach(card => {
|
||||
const text = card.textContent.toLowerCase();
|
||||
card.style.display = text.includes(searchTerm) ? 'block' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// Sort functionality
|
||||
const sortSelect = document.getElementById('sortSelect');
|
||||
|
||||
sortSelect.addEventListener('change', (e) => {
|
||||
const sortBy = e.target.value;
|
||||
const container = document.getElementById('resources-container');
|
||||
const cards = Array.from(container.getElementsByClassName('resource-card'));
|
||||
|
||||
cards.sort((a, b) => {
|
||||
if (sortBy === 'stars') {
|
||||
return parseInt(b.dataset.stars) - parseInt(a.dataset.stars);
|
||||
} else if (sortBy === 'name') {
|
||||
return a.querySelector('h3').textContent.localeCompare(b.querySelector('h3').textContent);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
|
||||
container.innerHTML = '';
|
||||
cards.forEach(card => container.appendChild(card));
|
||||
});
|
||||
|
||||
// Function to parse resources from README content
|
||||
function parseResources(content) {
|
||||
const resources = {
|
||||
'most-popular': [],
|
||||
'protocol': [],
|
||||
'relays': [],
|
||||
'clients': [],
|
||||
'libraries': [],
|
||||
'bridges-and-gateways': [],
|
||||
'cache-services': [],
|
||||
'tools': [],
|
||||
'nip-05-identity-services': [],
|
||||
'offline-signers': [],
|
||||
'vanity-pubkey-mining': [],
|
||||
'peer-to-peer-markets': [],
|
||||
'nip-07-browser-extensions': [],
|
||||
'nip-47-nostr-wallet-connect-nwc-implementations': [],
|
||||
'nip-57-zaps-compatible-wallets-and-solutions': [],
|
||||
'nip-90-data-vending-machines': [],
|
||||
'nip-96-file-storage-servers': [],
|
||||
'nostr-web-services-nws': [],
|
||||
'adjacent-protocols': [],
|
||||
'games-on-nostr': [],
|
||||
'communities': [],
|
||||
'tutorials': [],
|
||||
'recommended-reading-watching': [],
|
||||
'podcasts': [],
|
||||
'other-links': [],
|
||||
'deprecated': [],
|
||||
'related-resources': [],
|
||||
'contributing': [],
|
||||
'contributors': []
|
||||
};
|
||||
|
||||
// Split content by lines and process each line
|
||||
const lines = content.split('\n');
|
||||
let currentMainSection = '';
|
||||
let currentSubSection = '';
|
||||
|
||||
lines.forEach(line => {
|
||||
// Detect main section headers (##)
|
||||
if (line.startsWith('## ')) {
|
||||
currentMainSection = line.slice(3).trim();
|
||||
currentSubSection = '';
|
||||
}
|
||||
// Detect subsection headers (###)
|
||||
else if (line.startsWith('### ')) {
|
||||
currentSubSection = line.slice(4).trim();
|
||||
}
|
||||
// Special handling for contributors section with HTML content
|
||||
else if (currentMainSection.toLowerCase() === 'contributors' && line.includes('<a')) {
|
||||
const resource = {
|
||||
name: 'Contributors List',
|
||||
description: 'GitHub contributors to the awesome-nostr repository',
|
||||
link: 'https://github.com/aljazceru/awesome-nostr/graphs/contributors',
|
||||
raw: line.trim()
|
||||
};
|
||||
resources['contributors'].push(resource);
|
||||
}
|
||||
// Parse regular resource lines (starting with '- [')
|
||||
else if (line.trim().startsWith('- [')) {
|
||||
const resource = parseResourceLine(line);
|
||||
if (resource) {
|
||||
// Convert section header to category ID format
|
||||
const categoryId = currentMainSection
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.replace(/(^-|-$)/g, '');
|
||||
|
||||
// Add resource to appropriate category if it exists
|
||||
if (resources[categoryId]) {
|
||||
resources[categoryId].push(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Log the parsed data for debugging
|
||||
console.log('Parsed resources:', resources);
|
||||
return resources;
|
||||
}
|
||||
|
||||
// Function to parse a single resource line
|
||||
function parseResourceLine(line) {
|
||||
// Regular expressions to extract information
|
||||
const nameRegex = /\[(.*?)\]/;
|
||||
const linkRegex = /\((.*?)\)/;
|
||||
const starsRegex = /!\[stars\].*?stars\/(.*?)\/.*?style=social/;
|
||||
const descriptionRegex = /\) - (.*?)(?=\[|$)/;
|
||||
|
||||
try {
|
||||
const name = nameRegex.exec(line)?.[1];
|
||||
const link = linkRegex.exec(line)?.[1];
|
||||
const stars = starsRegex.exec(line)?.[1];
|
||||
const description = descriptionRegex.exec(line)?.[1]?.trim();
|
||||
|
||||
if (name && (link || description)) {
|
||||
return {
|
||||
name,
|
||||
link,
|
||||
stars: stars || 0,
|
||||
description: description || '',
|
||||
raw: line.trim() // Keep the original markdown line
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error parsing resource line:', error);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Modified createResourceCard function to display markdown links in a cleaner format
|
||||
function createResourceCard(resource) {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'resource-card';
|
||||
card.dataset.stars = resource.stars || 0;
|
||||
|
||||
// Create a formatted version of the markdown content
|
||||
const formattedContent = `
|
||||
<div class="resource-title"><strong>${resource.name}</strong></div>
|
||||
${resource.link ? `
|
||||
<div class="resource-link">
|
||||
<a href="${resource.link}" target="_blank">
|
||||
<i class="fas fa-external-link-alt"></i>
|
||||
${resource.link}
|
||||
</a>
|
||||
</div>
|
||||
` : ''}
|
||||
${resource.description ? `
|
||||
<div class="resource-description">
|
||||
${resource.description}
|
||||
</div>
|
||||
` : ''}
|
||||
${resource.stars ? `
|
||||
<div class="resource-stars">
|
||||
<i class="fas fa-star"></i>
|
||||
${resource.stars}
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
|
||||
card.innerHTML = formattedContent;
|
||||
return card;
|
||||
}
|
||||
|
||||
// Function to populate resources container
|
||||
function populateResources(categoryId, resources) {
|
||||
const container = document.getElementById('resources-container');
|
||||
container.innerHTML = ''; // Clear existing content
|
||||
|
||||
resources[categoryId]?.forEach(resource => {
|
||||
const card = createResourceCard(resource);
|
||||
container.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
// Function to get icon for category
|
||||
function getCategoryIcon(category) {
|
||||
const iconMap = {
|
||||
'most-popular': 'fa-star',
|
||||
'protocol': 'fa-book',
|
||||
'relays': 'fa-server',
|
||||
'clients': 'fa-mobile-alt',
|
||||
'libraries': 'fa-code',
|
||||
'bridges-and-gateways': 'fa-bridge',
|
||||
'cache-services': 'fa-database',
|
||||
'tools': 'fa-wrench',
|
||||
'nip-05-identity-services': 'fa-id-card',
|
||||
'offline-signers': 'fa-signature',
|
||||
'vanity-pubkey-mining': 'fa-hammer',
|
||||
'peer-to-peer-markets': 'fa-store',
|
||||
'nip-07-browser-extensions': 'fa-puzzle-piece',
|
||||
'nip-47-nostr-wallet-connect-nwc-implementations': 'fa-wallet',
|
||||
'nip-57-zaps-compatible-wallets-and-solutions': 'fa-bolt',
|
||||
'nip-90-data-vending-machines': 'fa-store-alt',
|
||||
'nip-96-file-storage-servers': 'fa-folder',
|
||||
'nostr-web-services-nws': 'fa-globe',
|
||||
'adjacent-protocols': 'fa-link',
|
||||
'games-on-nostr': 'fa-gamepad',
|
||||
'communities': 'fa-users',
|
||||
'tutorials': 'fa-graduation-cap',
|
||||
'recommended-reading-watching': 'fa-book-reader',
|
||||
'podcasts': 'fa-podcast',
|
||||
'other-links': 'fa-external-link-alt',
|
||||
'deprecated': 'fa-archive',
|
||||
'related-resources': 'fa-project-diagram',
|
||||
'contributing': 'fa-hands-helping',
|
||||
'contributors': 'fa-users-cog',
|
||||
// Default icon if category not found
|
||||
'default': 'fa-circle'
|
||||
};
|
||||
|
||||
const key = category.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
||||
return iconMap[key] || iconMap.default;
|
||||
}
|
||||
|
||||
// Function to generate navigation from content
|
||||
function generateNavigation(content) {
|
||||
const navList = document.querySelector('.nav-links');
|
||||
const categories = [];
|
||||
let currentCategory = '';
|
||||
|
||||
// Split content by lines and find all ## headers
|
||||
const lines = content.split('\n');
|
||||
lines.forEach(line => {
|
||||
if (line.startsWith('## ')) {
|
||||
currentCategory = line.slice(3).trim();
|
||||
const categoryId = currentCategory.toLowerCase().replace(/[^a-z0-9]+/g, '-');
|
||||
const icon = getCategoryIcon(currentCategory.toLowerCase());
|
||||
|
||||
categories.push({
|
||||
id: categoryId,
|
||||
name: currentCategory,
|
||||
icon: icon
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Generate navigation HTML
|
||||
navList.innerHTML = categories.map(category => `
|
||||
<li>
|
||||
<a href="#${category.id}">
|
||||
<i class="fas ${category.icon}"></i>
|
||||
${category.name}
|
||||
</a>
|
||||
</li>
|
||||
`).join('');
|
||||
|
||||
// Add click event listeners to the new navigation items
|
||||
document.querySelectorAll('.nav-links a').forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const categoryId = e.target.getAttribute('href').replace('#', '');
|
||||
populateResources(categoryId, window.parsedResources);
|
||||
});
|
||||
});
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
// Update the fetch call to generate navigation
|
||||
fetch('./README.md')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(content => {
|
||||
console.log('README content loaded:', content.substring(0, 200) + '...');
|
||||
|
||||
// Generate navigation first
|
||||
const categories = generateNavigation(content);
|
||||
|
||||
// Then parse and populate resources
|
||||
window.parsedResources = parseResources(content);
|
||||
console.log('Parsed resources:', window.parsedResources);
|
||||
|
||||
// Load initial category (first one in the list)
|
||||
if (categories.length > 0) {
|
||||
populateResources(categories[0].id, window.parsedResources);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading README:', error);
|
||||
document.getElementById('resources-container').innerHTML =
|
||||
`<div class="error-message">Error loading resources: ${error.message}</div>`;
|
||||
});
|
213
styles.css
Normal file
213
styles.css
Normal file
@ -0,0 +1,213 @@
|
||||
:root {
|
||||
--primary-color: #6e5494;
|
||||
--background-color: #ffffff;
|
||||
--text-color: #333333;
|
||||
--card-background: #f5f5f5;
|
||||
--sidebar-background: #f8f9fa;
|
||||
--hover-color: #563d7c;
|
||||
}
|
||||
|
||||
/* Dark theme variables */
|
||||
[data-theme="dark"] {
|
||||
--background-color: #1a1a1a;
|
||||
--text-color: #ffffff;
|
||||
--card-background: #2d2d2d;
|
||||
--sidebar-background: #252525;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* Sidebar Styles */
|
||||
.sidebar {
|
||||
width: 280px;
|
||||
background-color: var(--sidebar-background);
|
||||
padding: 1rem;
|
||||
position: fixed;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
position: relative;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 100%;
|
||||
padding: 0.5rem 2rem 0.5rem 1rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.search-box i {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.nav-links li {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-links a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem;
|
||||
color: var(--text-color);
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.nav-links a:hover {
|
||||
background-color: var(--primary-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-links i {
|
||||
margin-right: 0.5rem;
|
||||
width: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Main Content Styles */
|
||||
.main-content {
|
||||
margin-left: 280px;
|
||||
padding: 2rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.view-controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Resource Cards */
|
||||
.resource-card {
|
||||
background: var(--card-bg);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.resource-title {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 8px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.resource-link {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.resource-link a {
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.resource-link a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.resource-description {
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 12px;
|
||||
line-height: 1.5;
|
||||
font-size: 0.95em;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid var(--border-color);
|
||||
}
|
||||
|
||||
.resource-stars {
|
||||
color: var(--star-color);
|
||||
font-size: 0.9em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.fa-star {
|
||||
color: #f1c40f;
|
||||
}
|
||||
|
||||
.fa-external-link-alt {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
transform: translateX(-100%);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.sidebar.active {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.menu-toggle {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-content {
|
||||
padding: 10px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown-content a {
|
||||
color: #0366d6;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.markdown-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
Loading…
Reference in New Issue
Block a user