From a609d89adc16afd70dfd3d62f38f8234c35b2cd4 Mon Sep 17 00:00:00 2001
From: Yeghro <130201060+Yeghro@users.noreply.github.com>
Date: Fri, 24 Jan 2025 18:29:12 -0800
Subject: [PATCH] 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
---
index.html | 54 +++++++++
script.js | 320 +++++++++++++++++++++++++++++++++++++++++++++++++++++
styles.css | 213 +++++++++++++++++++++++++++++++++++
3 files changed, 587 insertions(+)
create mode 100644 index.html
create mode 100644 script.js
create mode 100644 styles.css
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..20c7e51
--- /dev/null
+++ b/index.html
@@ -0,0 +1,54 @@
+
+
+
+
+
+ Awesome Nostr Resources
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..dd64bea
--- /dev/null
+++ b/script.js
@@ -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'
+ ? ' '
+ : ' ';
+});
+
+// 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('${resource.name}
+ ${resource.link ? `
+
+ ` : ''}
+ ${resource.description ? `
+
+ ${resource.description}
+
+ ` : ''}
+ ${resource.stars ? `
+
+
+ ${resource.stars}
+
+ ` : ''}
+ `;
+
+ 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 => `
+
+
+
+ ${category.name}
+
+
+ `).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 =
+ `Error loading resources: ${error.message}
`;
+ });
\ No newline at end of file
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..8e196d0
--- /dev/null
+++ b/styles.css
@@ -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;
+}
\ No newline at end of file