Upload 3 files
Browse files- js/pages/AreaPage.js +151 -237
- js/pages/HomePage.js +147 -118
- js/pages/ResourcesPage.js +1 -21
js/pages/AreaPage.js
CHANGED
|
@@ -1,135 +1,175 @@
|
|
| 1 |
-
// pages/AreaPage.js -
|
| 2 |
import { areasData } from '../data/areas.js';
|
| 3 |
import { featuredArtifacts } from '../data/artifacts.js';
|
| 4 |
import { createArtifactCarousel } from '../cards/ArtifactSummaryCard.js';
|
| 5 |
import { sampleResources } from '../data/resources.js';
|
| 6 |
import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
|
| 7 |
-
import { renderSidebar } from '../utils/sidebar.js'; // Import the new sidebar component
|
| 8 |
|
| 9 |
-
export function renderAreaPage(areaId) {
|
| 10 |
const area = areasData[areaId];
|
| 11 |
if (!area) {
|
| 12 |
return {
|
| 13 |
-
content: `<div class="bg-white rounded-lg shadow-sm p-8"><h1 class="text-2xl font-bold text-red-600">Area not found</h1></div>`,
|
| 14 |
init: () => {}
|
| 15 |
};
|
| 16 |
}
|
| 17 |
|
| 18 |
-
//
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
|
| 23 |
-
//
|
| 24 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
id: key,
|
| 26 |
name: typeof value === 'string' ? value : value.name,
|
|
|
|
| 27 |
description: typeof value === 'string' ? '' : (value.description || ''),
|
| 28 |
-
|
| 29 |
-
gradient: typeof value === 'object' ? value.gradient : null
|
| 30 |
}));
|
| 31 |
|
| 32 |
const content = `
|
| 33 |
-
<!--
|
| 34 |
-
<
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
</div>
|
| 39 |
-
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
-
<!-- Description -->
|
| 43 |
<div class="mb-8">
|
| 44 |
-
<p class="text-gray-700 leading-relaxed
|
| 45 |
</div>
|
| 46 |
|
| 47 |
<!-- Role of Openness -->
|
| 48 |
-
|
| 49 |
-
|
|
|
|
| 50 |
<p class="text-orange-800 italic leading-relaxed">${area.openness}</p>
|
| 51 |
</div>
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
<
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
</div>
|
| 66 |
</div>
|
| 67 |
</div>
|
| 68 |
</section>
|
|
|
|
| 69 |
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
${subArea.description ? `
|
| 77 |
-
<div class="mb-6">
|
| 78 |
-
<p class="text-gray-700 leading-relaxed">${subArea.description}</p>
|
| 79 |
-
</div>
|
| 80 |
-
` : ''}
|
| 81 |
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
</div>
|
| 97 |
-
</section>
|
| 98 |
-
`).join('')}
|
| 99 |
|
| 100 |
-
|
| 101 |
-
<section id="resources" class="mb-12 relative z-10">
|
| 102 |
-
<div class="bg-white bg-opacity-90 backdrop-blur-sm rounded-lg shadow-sm p-8">
|
| 103 |
-
<h2 class="text-2xl font-bold text-gray-900 mb-6">Technical Resources</h2>
|
| 104 |
|
| 105 |
-
<!--
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
</p>
|
| 110 |
</div>
|
|
|
|
| 111 |
|
| 112 |
-
<!--
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 124 |
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
|
| 125 |
-
</svg>
|
| 126 |
-
</button>
|
| 127 |
-
<button class="resource-carousel-next bg-gray-200 hover:bg-gray-300 text-gray-700 px-3 py-2 rounded-md transition-colors">
|
| 128 |
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 129 |
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
| 130 |
-
</svg>
|
| 131 |
-
</button>
|
| 132 |
-
</div>
|
| 133 |
</div>
|
| 134 |
</div>
|
| 135 |
</section>
|
|
@@ -138,158 +178,32 @@ export function renderAreaPage(areaId) {
|
|
| 138 |
return {
|
| 139 |
content,
|
| 140 |
init: () => {
|
| 141 |
-
// Initialize artifact
|
| 142 |
-
// to ensure DOM elements are fully rendered
|
| 143 |
setTimeout(async () => {
|
| 144 |
try {
|
| 145 |
-
console.log('Accessing artifacts from global scope...');
|
| 146 |
-
// Access artifacts from global scope
|
| 147 |
const allArtifacts = window.allArtifacts || [];
|
| 148 |
-
if (allArtifacts.length === 0) {
|
| 149 |
-
console.warn('Global artifacts data not available or empty. Falling back to featuredArtifacts.');
|
| 150 |
-
}
|
| 151 |
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
console.log(`Filtered artifacts for ${subArea.id}:`, transformedArtifacts.length);
|
| 168 |
-
const carouselId = `${subArea.id}-artifacts-carousel`;
|
| 169 |
-
createArtifactCarousel(transformedArtifacts, carouselId);
|
| 170 |
-
} catch (error) {
|
| 171 |
-
console.error(`Error creating carousel for ${subArea.id}:`, error);
|
| 172 |
-
// Fallback to featured artifacts if filtering fails
|
| 173 |
-
const carouselId = `${subArea.id}-artifacts-carousel`;
|
| 174 |
-
createArtifactCarousel(featuredArtifacts, carouselId);
|
| 175 |
-
}
|
| 176 |
-
});
|
| 177 |
} catch (error) {
|
| 178 |
-
console.error(
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
const carouselId = `${subArea.id}-artifacts-carousel`;
|
| 182 |
-
createArtifactCarousel(featuredArtifacts, carouselId);
|
| 183 |
-
});
|
| 184 |
}
|
| 185 |
}, 50);
|
| 186 |
-
|
| 187 |
-
// Initialize resource cards
|
| 188 |
-
initializeResourceCards();
|
| 189 |
-
|
| 190 |
-
// Initialize resource carousel if there are resources
|
| 191 |
-
if (areaResources.length > 0) {
|
| 192 |
-
initializeAreaResourceCarousel(areaResources);
|
| 193 |
-
}
|
| 194 |
-
|
| 195 |
-
// Initialize background attribution
|
| 196 |
-
initializeAreaBackgroundAttribution();
|
| 197 |
}
|
| 198 |
};
|
| 199 |
}
|
| 200 |
-
|
| 201 |
-
export function getAreaPageSidebar(areaId) {
|
| 202 |
-
const area = areasData[areaId];
|
| 203 |
-
if (!area) {
|
| 204 |
-
return '<div class="p-4">Area not found</div>';
|
| 205 |
-
}
|
| 206 |
-
|
| 207 |
-
const subAreas = Object.entries(area.subAreas).map(([key, value]) => ({
|
| 208 |
-
id: key,
|
| 209 |
-
name: typeof value === 'string' ? value : value.name
|
| 210 |
-
}));
|
| 211 |
-
|
| 212 |
-
const sidebarItems = [
|
| 213 |
-
{
|
| 214 |
-
label: 'Overview',
|
| 215 |
-
href: '#overview',
|
| 216 |
-
isNested: false,
|
| 217 |
-
isActive: window.location.hash === '#overview',
|
| 218 |
-
},
|
| 219 |
-
{
|
| 220 |
-
label: 'Topics',
|
| 221 |
-
isHeader: true,
|
| 222 |
-
isNested: true,
|
| 223 |
-
subItems: subAreas.map(subArea => ({
|
| 224 |
-
label: subArea.name,
|
| 225 |
-
href: `#${subArea.id}`,
|
| 226 |
-
isActive: window.location.hash === `#${subArea.id}`,
|
| 227 |
-
})),
|
| 228 |
-
},
|
| 229 |
-
{
|
| 230 |
-
label: 'Resources',
|
| 231 |
-
href: '#resources',
|
| 232 |
-
isNested: false,
|
| 233 |
-
isActive: window.location.hash === '#resources',
|
| 234 |
-
},
|
| 235 |
-
];
|
| 236 |
-
|
| 237 |
-
return renderSidebar('On This Page', sidebarItems);
|
| 238 |
-
}
|
| 239 |
-
|
| 240 |
-
function initializeAreaResourceCarousel(resources) {
|
| 241 |
-
const carousel = document.querySelector('.resource-carousel');
|
| 242 |
-
const prevButton = document.querySelector('.resource-carousel-prev');
|
| 243 |
-
const nextButton = document.querySelector('.resource-carousel-next');
|
| 244 |
-
|
| 245 |
-
if (!carousel || !prevButton || !nextButton) {
|
| 246 |
-
return;
|
| 247 |
-
}
|
| 248 |
-
|
| 249 |
-
let currentIndex = 0;
|
| 250 |
-
const cardWidth = 624; // 600px card + 24px gap
|
| 251 |
-
const visibleCards = 1.5; // Show 1.5 cards at a time (larger cards)
|
| 252 |
-
const maxIndex = resources.length - 1; // Allow going to the last item
|
| 253 |
-
|
| 254 |
-
function updateCarousel() {
|
| 255 |
-
const translateX = -currentIndex * cardWidth;
|
| 256 |
-
carousel.style.transform = `translateX(${translateX}px)`;
|
| 257 |
-
|
| 258 |
-
// Update button states - no disabled states for looping
|
| 259 |
-
prevButton.classList.remove('opacity-50', 'cursor-not-allowed');
|
| 260 |
-
nextButton.classList.remove('opacity-50', 'cursor-not-allowed');
|
| 261 |
-
}
|
| 262 |
-
|
| 263 |
-
prevButton.addEventListener('click', () => {
|
| 264 |
-
currentIndex = currentIndex > 0 ? currentIndex - 1 : maxIndex;
|
| 265 |
-
updateCarousel();
|
| 266 |
-
});
|
| 267 |
-
|
| 268 |
-
nextButton.addEventListener('click', () => {
|
| 269 |
-
currentIndex = currentIndex < maxIndex ? currentIndex + 1 : 0;
|
| 270 |
-
updateCarousel();
|
| 271 |
-
});
|
| 272 |
-
|
| 273 |
-
// Initialize carousel state
|
| 274 |
-
updateCarousel();
|
| 275 |
-
}
|
| 276 |
-
|
| 277 |
-
function initializeAreaBackgroundAttribution() {
|
| 278 |
-
const backgroundContainer = document.getElementById('area-background-container');
|
| 279 |
-
const attribution = document.getElementById('area-bg-attribution');
|
| 280 |
-
|
| 281 |
-
if (!backgroundContainer || !attribution) {
|
| 282 |
-
return;
|
| 283 |
-
}
|
| 284 |
-
|
| 285 |
-
// Show attribution on hover over the background container
|
| 286 |
-
backgroundContainer.addEventListener('mouseenter', () => {
|
| 287 |
-
attribution.style.opacity = '1';
|
| 288 |
-
});
|
| 289 |
-
|
| 290 |
-
backgroundContainer.addEventListener('mouseleave', () => {
|
| 291 |
-
attribution.style.opacity = '0';
|
| 292 |
-
});
|
| 293 |
-
}
|
| 294 |
-
|
| 295 |
-
// Colors are now defined in the areas data and accessed via subArea.gradient
|
|
|
|
| 1 |
+
// pages/AreaPage.js - Single-topic view for area pages
|
| 2 |
import { areasData } from '../data/areas.js';
|
| 3 |
import { featuredArtifacts } from '../data/artifacts.js';
|
| 4 |
import { createArtifactCarousel } from '../cards/ArtifactSummaryCard.js';
|
| 5 |
import { sampleResources } from '../data/resources.js';
|
| 6 |
import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
|
|
|
|
| 7 |
|
| 8 |
+
export function renderAreaPage(areaId, topicId = null) {
|
| 9 |
const area = areasData[areaId];
|
| 10 |
if (!area) {
|
| 11 |
return {
|
| 12 |
+
content: `<div class="bg-white/90 backdrop-blur-md rounded-lg shadow-sm p-8 mx-4"><h1 class="text-2xl font-bold text-red-600">Area not found</h1></div>`,
|
| 13 |
init: () => {}
|
| 14 |
};
|
| 15 |
}
|
| 16 |
|
| 17 |
+
// If no topic specified, show overview of the area
|
| 18 |
+
if (!topicId) {
|
| 19 |
+
return renderAreaOverview(area, areaId);
|
| 20 |
+
}
|
| 21 |
|
| 22 |
+
// Show specific topic
|
| 23 |
+
const topic = area.subAreas[topicId];
|
| 24 |
+
if (!topic) {
|
| 25 |
+
return {
|
| 26 |
+
content: `<div class="bg-white/90 backdrop-blur-md rounded-lg shadow-sm p-8 mx-4"><h1 class="text-2xl font-bold text-red-600">Topic not found</h1></div>`,
|
| 27 |
+
init: () => {}
|
| 28 |
+
};
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
return renderTopicView(area, areaId, topic, topicId);
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
function renderAreaOverview(area, areaId) {
|
| 35 |
+
// Get sub-areas as array
|
| 36 |
+
const topics = Object.entries(area.subAreas).map(([key, value]) => ({
|
| 37 |
id: key,
|
| 38 |
name: typeof value === 'string' ? value : value.name,
|
| 39 |
+
navName: typeof value === 'string' ? value : (value.navName || value.name),
|
| 40 |
description: typeof value === 'string' ? '' : (value.description || ''),
|
| 41 |
+
color: typeof value === 'object' ? value.color : 'bg-gray-200 text-gray-700'
|
|
|
|
| 42 |
}));
|
| 43 |
|
| 44 |
const content = `
|
| 45 |
+
<!-- Top spacing -->
|
| 46 |
+
<div class="mb-8"></div>
|
| 47 |
+
|
| 48 |
+
<!-- Page Navigation Card -->
|
| 49 |
+
<section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 50 |
+
<div class="bg-white/70 backdrop-blur-sm shadow-sm rounded-lg px-6 py-4 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 51 |
+
<div class="flex items-center gap-3 overflow-x-auto">
|
| 52 |
+
<span class="text-gray-600 font-semibold whitespace-nowrap">${area.navTitle}:</span>
|
| 53 |
+
<a href="/${areaId}" class="px-3 py-1.5 text-sm font-semibold text-blue-600 underline decoration-2 underline-offset-2 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Overview</a>
|
| 54 |
+
${topics.map((topic, index) => `
|
| 55 |
+
${index === 0 ? '<span class="text-gray-300">•</span>' : ''}
|
| 56 |
+
<a href="/${areaId}/${topic.id}" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">${topic.navName}</a>
|
| 57 |
+
${index < topics.length - 1 ? '<span class="text-gray-300">•</span>' : ''}
|
| 58 |
+
`).join('')}
|
| 59 |
+
</div>
|
| 60 |
</div>
|
| 61 |
+
</section>
|
| 62 |
+
|
| 63 |
+
<!-- Area Overview Section -->
|
| 64 |
+
<section id="overview" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 65 |
+
<div class="bg-white/90 backdrop-blur-md shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 66 |
+
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">${area.title}</h1>
|
| 67 |
|
| 68 |
+
<!-- Area Description -->
|
| 69 |
<div class="mb-8">
|
| 70 |
+
<p class="text-gray-700 leading-relaxed">${area.description}</p>
|
| 71 |
</div>
|
| 72 |
|
| 73 |
<!-- Role of Openness -->
|
| 74 |
+
${area.openness ? `
|
| 75 |
+
<div class="px-4 sm:px-6 py-5 bg-gradient-to-r from-orange-50/95 to-yellow-50/95 backdrop-blur-sm border-l-4 border-orange-400 rounded-r-lg mb-8">
|
| 76 |
+
<p class="font-bold text-orange-900 mb-3 text-lg">🤗 The Role of Openness</p>
|
| 77 |
<p class="text-orange-800 italic leading-relaxed">${area.openness}</p>
|
| 78 |
</div>
|
| 79 |
+
` : ''}
|
| 80 |
+
|
| 81 |
+
<!-- Topics within this area -->
|
| 82 |
+
<div>
|
| 83 |
+
<p class="text-xs font-semibold text-gray-600 uppercase tracking-wide mb-3">Topics in this area:</p>
|
| 84 |
+
<div class="flex flex-wrap gap-2">
|
| 85 |
+
${topics.map(topic => {
|
| 86 |
+
// Extract colors from topic.color
|
| 87 |
+
let bgColor = 'bg-gray-200';
|
| 88 |
+
let textColor = 'text-gray-700';
|
| 89 |
+
if (topic.color) {
|
| 90 |
+
const bgMatch = topic.color.match(/bg-(\w+)-(\d+)/);
|
| 91 |
+
const textMatch = topic.color.match(/text-(\w+)-(\d+)/);
|
| 92 |
+
if (bgMatch) bgColor = `bg-${bgMatch[1]}-${bgMatch[2]}`;
|
| 93 |
+
if (textMatch) textColor = `text-${textMatch[1]}-700`;
|
| 94 |
+
}
|
| 95 |
+
return `
|
| 96 |
+
<a href="/${areaId}/${topic.id}" class="inline-block px-3 py-1.5 text-sm ${bgColor} ${textColor} rounded hover:opacity-80 transition-opacity">
|
| 97 |
+
${topic.navName}
|
| 98 |
+
</a>
|
| 99 |
+
`;
|
| 100 |
+
}).join('')}
|
| 101 |
</div>
|
| 102 |
</div>
|
| 103 |
</div>
|
| 104 |
</section>
|
| 105 |
+
`;
|
| 106 |
|
| 107 |
+
return {
|
| 108 |
+
content,
|
| 109 |
+
init: () => {}
|
| 110 |
+
};
|
| 111 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
+
function renderTopicView(area, areaId, topic, topicId) {
|
| 114 |
+
const topicName = topic.navName || topic.name;
|
| 115 |
+
const topicDescription = topic.description || '';
|
| 116 |
+
const topicOpenness = topic.openness || '';
|
| 117 |
+
|
| 118 |
+
// Get all topics for navigation
|
| 119 |
+
const allTopics = Object.entries(area.subAreas).map(([key, value]) => ({
|
| 120 |
+
id: key,
|
| 121 |
+
navName: typeof value === 'string' ? value : (value.navName || value.name)
|
| 122 |
+
}));
|
| 123 |
|
| 124 |
+
const content = `
|
| 125 |
+
<!-- Top spacing -->
|
| 126 |
+
<div class="mb-8"></div>
|
| 127 |
+
|
| 128 |
+
<!-- Page Navigation Card -->
|
| 129 |
+
<section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 130 |
+
<div class="bg-white/70 backdrop-blur-sm shadow-sm rounded-lg px-6 py-4 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 131 |
+
<div class="flex items-center gap-3 overflow-x-auto">
|
| 132 |
+
<span class="text-gray-600 font-semibold whitespace-nowrap">${area.navTitle}:</span>
|
| 133 |
+
<a href="/${areaId}" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Overview</a>
|
| 134 |
+
${allTopics.map((t, index) => `
|
| 135 |
+
${index === 0 ? '<span class="text-gray-300">•</span>' : ''}
|
| 136 |
+
<a href="/${areaId}/${t.id}" class="px-3 py-1.5 text-sm font-medium transition-colors whitespace-nowrap rounded ${t.id === topicId ? 'text-blue-600 font-semibold underline decoration-2 underline-offset-2' : 'text-gray-700 hover:text-blue-600 hover:bg-blue-50'}">${t.navName}</a>
|
| 137 |
+
${index < allTopics.length - 1 ? '<span class="text-gray-300">•</span>' : ''}
|
| 138 |
+
`).join('')}
|
| 139 |
+
</div>
|
| 140 |
+
</div>
|
| 141 |
+
</section>
|
| 142 |
+
|
| 143 |
+
<!-- Topic Content Section -->
|
| 144 |
+
<section id="${topicId}" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 145 |
+
<div class="bg-white/80 backdrop-blur-md shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 146 |
+
<!-- Breadcrumb -->
|
| 147 |
+
<div class="mb-4 text-sm text-gray-600">
|
| 148 |
+
<a href="/${areaId}" class="hover:text-blue-600 transition-colors">${area.navTitle}</a>
|
| 149 |
+
<span class="mx-2">›</span>
|
| 150 |
+
<span class="text-gray-900 font-medium">${topicName}</span>
|
| 151 |
</div>
|
|
|
|
|
|
|
| 152 |
|
| 153 |
+
<h1 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">${topic.name}</h1>
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
+
<!-- Topic Description -->
|
| 156 |
+
${topicDescription ? `
|
| 157 |
+
<div class="mb-8">
|
| 158 |
+
<p class="text-gray-700 leading-relaxed">${topicDescription}</p>
|
|
|
|
| 159 |
</div>
|
| 160 |
+
` : ''}
|
| 161 |
|
| 162 |
+
<!-- Role of Openness for this topic -->
|
| 163 |
+
${topicOpenness ? `
|
| 164 |
+
<div class="px-4 sm:px-6 py-5 bg-gradient-to-r from-orange-50/95 to-yellow-50/95 backdrop-blur-sm border-l-4 border-orange-400 rounded-r-lg mb-8">
|
| 165 |
+
<p class="font-bold text-orange-900 mb-3 text-lg">🤗 The Role of Openness</p>
|
| 166 |
+
<p class="text-orange-800 italic leading-relaxed">${topicOpenness}</p>
|
| 167 |
+
</div>
|
| 168 |
+
` : ''}
|
| 169 |
+
|
| 170 |
+
<h3 class="text-xl md:text-2xl font-bold text-gray-900 mb-6">Related Research & Resources</h3>
|
| 171 |
+
<div id="${topicId}-artifacts-carousel" class="overflow-x-auto -mx-2">
|
| 172 |
+
<!-- Carousel will be inserted here -->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
</div>
|
| 174 |
</div>
|
| 175 |
</section>
|
|
|
|
| 178 |
return {
|
| 179 |
content,
|
| 180 |
init: () => {
|
| 181 |
+
// Initialize artifact carousel for this topic
|
|
|
|
| 182 |
setTimeout(async () => {
|
| 183 |
try {
|
|
|
|
|
|
|
| 184 |
const allArtifacts = window.allArtifacts || [];
|
|
|
|
|
|
|
|
|
|
| 185 |
|
| 186 |
+
// Filter artifacts by this specific topic
|
| 187 |
+
const filteredArtifacts = allArtifacts.filter(artifact =>
|
| 188 |
+
artifact.topics && artifact.topics.includes(topicId)
|
| 189 |
+
);
|
| 190 |
+
|
| 191 |
+
// Transform field names to match expected format
|
| 192 |
+
const transformedArtifacts = filteredArtifacts.map(artifact => ({
|
| 193 |
+
...artifact,
|
| 194 |
+
areaTags: artifact.areas,
|
| 195 |
+
subAreaTags: artifact.topics,
|
| 196 |
+
sourceUrl: artifact.url
|
| 197 |
+
}));
|
| 198 |
+
|
| 199 |
+
const carouselId = `${topicId}-artifacts-carousel`;
|
| 200 |
+
createArtifactCarousel(transformedArtifacts.length > 0 ? transformedArtifacts : featuredArtifacts, carouselId);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
} catch (error) {
|
| 202 |
+
console.error(`Error creating carousel for ${topicId}:`, error);
|
| 203 |
+
const carouselId = `${topicId}-artifacts-carousel`;
|
| 204 |
+
createArtifactCarousel(featuredArtifacts, carouselId);
|
|
|
|
|
|
|
|
|
|
| 205 |
}
|
| 206 |
}, 50);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
}
|
| 208 |
};
|
| 209 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
js/pages/HomePage.js
CHANGED
|
@@ -5,83 +5,89 @@ import { createArtifactCarousel } from '../cards/ArtifactSummaryCard.js';
|
|
| 5 |
import { areasData, homeBackgroundImage } from '../data/areas.js';
|
| 6 |
import { featuredArtifacts } from '../data/artifacts.js';
|
| 7 |
import { teamMembers } from '../data/team.js';
|
| 8 |
-
import { renderSidebar } from '../utils/sidebar.js'; // Import the new sidebar component
|
| 9 |
|
| 10 |
export function renderHomePage() {
|
| 11 |
const content = `
|
| 12 |
-
<!--
|
| 13 |
-
<
|
| 14 |
-
<!-- Part 1: Team Introduction and Research Areas -->
|
| 15 |
-
<div class="bg-white rounded-lg shadow-sm p-8 mb-6">
|
| 16 |
-
<h3 class="text-3xl font-bold text-gray-900 mb-6">Team Introduction</h3>
|
| 17 |
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
<div class="mb-8">
|
| 20 |
-
<
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
${Object.values(areasData).map(area => `
|
| 25 |
-
<a
|
| 26 |
-
href="#${area.id}"
|
| 27 |
-
class="group p-4 bg-gradient-to-br ${area.colors.gradient} rounded-lg border transition-all duration-200 text-left block no-underline"
|
| 28 |
-
>
|
| 29 |
-
<h4 class="font-semibold text-sm mb-1 group-hover:scale-105 transition-transform">${area.title}</h4>
|
| 30 |
-
<p class="text-xs opacity-80">${getAreaShortDescription(area.id)}</p>
|
| 31 |
-
</a>
|
| 32 |
-
`).join('')}
|
| 33 |
-
</div>
|
| 34 |
-
|
| 35 |
-
<!-- Perspective and Positionality -->
|
| 36 |
-
<div class="mt-8 px-4 py-4 bg-gradient-to-r from-orange-50 to-yellow-50 border-l-4 border-orange-300 rounded-r-lg">
|
| 37 |
-
<p class="font-bold text-center text-orange-900 mb-2">🤗 Perspective and Positionality</p>
|
| 38 |
-
<p class="text-orange-800 italic leading-relaxed">
|
| 39 |
-
Our work on these topics is shaped by the context of our work at Hugging Face, which is the main platform for open and collaborative development and sharing of Artificial Intelligence artifacts. As a result, much of our work discusses the specific roles of openness and transparency in shaping AI technology into a more equitable and better-governed category of technology.
|
| 40 |
</p>
|
| 41 |
</div>
|
| 42 |
</div>
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
<div id="featured-artifacts-carousel">
|
| 51 |
-
<!-- Carousel will be inserted here -->
|
| 52 |
-
</div>
|
| 53 |
-
</div>
|
| 54 |
</div>
|
|
|
|
|
|
|
| 55 |
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
<!-- Team members will be inserted here by JavaScript -->
|
| 63 |
-
</div>
|
| 64 |
-
</div>
|
| 65 |
-
<div class="prose text-gray-700">
|
| 66 |
-
<p>We also work closely with
|
| 67 |
-
<a href="https://huggingface.co/irenesolaiman" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Irene Solaiman</a> (Chief Policy Officer),
|
| 68 |
-
<a href="https://huggingface.co/evijit" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Avijit Ghosh</a> (Applied Policy Researcher) in the policy team,
|
| 69 |
-
and with <a href="https://huggingface.co/meg" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Meg Mitchell</a> (Chief Ethics Scientist),
|
| 70 |
-
and with <a href="https://huggingface.co/brunatrevelin" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Bruna Trevelin</a> (Legal Counsel)!
|
| 71 |
-
</p>
|
| 72 |
-
</div>
|
| 73 |
</div>
|
| 74 |
</div>
|
| 75 |
</section>
|
| 76 |
|
| 77 |
-
<!-- Research Areas
|
| 78 |
-
<
|
| 79 |
-
<
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
<!--
|
| 83 |
-
<section id="
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
</section>
|
| 86 |
`;
|
| 87 |
|
|
@@ -96,59 +102,6 @@ export function renderHomePage() {
|
|
| 96 |
};
|
| 97 |
}
|
| 98 |
|
| 99 |
-
export function getHomePageSidebar() {
|
| 100 |
-
const sidebarItems = [
|
| 101 |
-
{
|
| 102 |
-
label: 'Team Introduction',
|
| 103 |
-
href: '#intro',
|
| 104 |
-
isNested: false,
|
| 105 |
-
isActive: window.location.hash === '#intro',
|
| 106 |
-
subItems: [
|
| 107 |
-
{
|
| 108 |
-
label: 'Recent & Featured Works',
|
| 109 |
-
href: '#recent-featured-works',
|
| 110 |
-
isActive: window.location.hash === '#recent-featured-works',
|
| 111 |
-
},
|
| 112 |
-
{
|
| 113 |
-
label: 'Team Members',
|
| 114 |
-
href: '#team-members',
|
| 115 |
-
isActive: window.location.hash === '#team-members',
|
| 116 |
-
},
|
| 117 |
-
],
|
| 118 |
-
},
|
| 119 |
-
{
|
| 120 |
-
label: 'Research Areas',
|
| 121 |
-
href: '#research-areas-header',
|
| 122 |
-
isNested: false,
|
| 123 |
-
isActive: window.location.hash === '#research-areas-header',
|
| 124 |
-
subItems: [
|
| 125 |
-
{
|
| 126 |
-
label: 'Efficiency & Environment',
|
| 127 |
-
href: '#efficiency',
|
| 128 |
-
isActive: window.location.hash === '#efficiency',
|
| 129 |
-
},
|
| 130 |
-
{
|
| 131 |
-
label: 'Consent & Personal Interactions',
|
| 132 |
-
href: '#personal',
|
| 133 |
-
isActive: window.location.hash === '#personal',
|
| 134 |
-
},
|
| 135 |
-
{
|
| 136 |
-
label: 'Rights & Regulation',
|
| 137 |
-
href: '#rights',
|
| 138 |
-
isActive: window.location.hash === '#rights',
|
| 139 |
-
},
|
| 140 |
-
{
|
| 141 |
-
label: 'Socio-Economic & Technical Ecosystems',
|
| 142 |
-
href: '#ecosystems',
|
| 143 |
-
isActive: window.location.hash === '#ecosystems',
|
| 144 |
-
},
|
| 145 |
-
],
|
| 146 |
-
},
|
| 147 |
-
];
|
| 148 |
-
|
| 149 |
-
return renderSidebar('On This Page', sidebarItems);
|
| 150 |
-
}
|
| 151 |
-
|
| 152 |
function initializeTeamMembers() {
|
| 153 |
const teamContainer = document.getElementById('team-grid');
|
| 154 |
if (!teamContainer) {
|
|
@@ -171,13 +124,89 @@ function getAreaShortDescription(areaId) {
|
|
| 171 |
}
|
| 172 |
|
| 173 |
function initializeHomeAreaCards() {
|
| 174 |
-
const areasContainer = document.getElementById('research-areas');
|
| 175 |
if (!areasContainer) return;
|
| 176 |
|
| 177 |
const areas = Object.values(areasData);
|
| 178 |
-
areasContainer.innerHTML = areas.map(area =>
|
| 179 |
-
|
| 180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 181 |
}
|
| 182 |
|
| 183 |
function initializeArtifactCarousels() {
|
|
|
|
| 5 |
import { areasData, homeBackgroundImage } from '../data/areas.js';
|
| 6 |
import { featuredArtifacts } from '../data/artifacts.js';
|
| 7 |
import { teamMembers } from '../data/team.js';
|
|
|
|
| 8 |
|
| 9 |
export function renderHomePage() {
|
| 10 |
const content = `
|
| 11 |
+
<!-- Top spacing -->
|
| 12 |
+
<div class="mb-8"></div>
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
<!-- Page Navigation Card -->
|
| 15 |
+
<section class="mb-8 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 16 |
+
<div class="bg-white/70 backdrop-blur-sm shadow-sm rounded-lg px-6 py-4 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 17 |
+
<div class="flex items-center gap-3 overflow-x-auto">
|
| 18 |
+
<span class="text-gray-600 font-semibold whitespace-nowrap">On this page:</span>
|
| 19 |
+
<a href="/#about" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">About</a>
|
| 20 |
+
<span class="text-gray-300">•</span>
|
| 21 |
+
<a href="/#recent-works" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Recent Works</a>
|
| 22 |
+
<span class="text-gray-300">•</span>
|
| 23 |
+
<a href="/#research-areas" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Research Areas</a>
|
| 24 |
+
<span class="text-gray-300">•</span>
|
| 25 |
+
<a href="/#team" class="px-3 py-1.5 text-sm font-medium text-gray-700 hover:text-blue-600 hover:bg-blue-50 rounded transition-colors whitespace-nowrap">Team Members</a>
|
| 26 |
+
</div>
|
| 27 |
+
</div>
|
| 28 |
+
</section>
|
| 29 |
+
|
| 30 |
+
<!-- About Section - Responsive Width with Frosted Glass -->
|
| 31 |
+
<section id="about" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 32 |
+
<div class="bg-white/40 backdrop-blur-sm shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 33 |
+
<h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">About</h2>
|
| 34 |
+
|
| 35 |
+
<!-- Introduction Text - Single column -->
|
| 36 |
<div class="mb-8">
|
| 37 |
+
<div>
|
| 38 |
+
<p class="text-gray-700 leading-relaxed">
|
| 39 |
+
We're a multidisciplinary team working on research and regulatory questions related to AI systems — their (open) development, governance, and impact on society at large.
|
| 40 |
+
Our work spans four key inter-connected areas where AI technology intersects with society:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
</p>
|
| 42 |
</div>
|
| 43 |
</div>
|
| 44 |
|
| 45 |
+
<!-- Perspective and Positionality -->
|
| 46 |
+
<div class="px-4 sm:px-6 py-5 bg-gradient-to-r from-orange-50/95 to-yellow-50/95 backdrop-blur-sm border-l-4 border-orange-400 rounded-r-lg">
|
| 47 |
+
<p class="font-bold text-orange-900 mb-3 text-lg">🤗 Perspective and Positionality</p>
|
| 48 |
+
<p class="text-orange-800 italic leading-relaxed">
|
| 49 |
+
Our work on these topics is shaped by the context of our work at Hugging Face, which is the main platform for open and collaborative development and sharing of Artificial Intelligence artifacts. As a result, much of our work discusses the specific roles of openness and transparency in shaping AI technology into a more equitable and better-governed category of technology.
|
| 50 |
+
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
</div>
|
| 52 |
+
</div>
|
| 53 |
+
</section>
|
| 54 |
|
| 55 |
+
<!-- Featured Works Section - Transparent Card -->
|
| 56 |
+
<section id="recent-works" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 57 |
+
<div class="bg-white/20 backdrop-blur-sm shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 58 |
+
<h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Recent & Featured Works</h2>
|
| 59 |
+
<div id="featured-artifacts-carousel" class="overflow-x-auto -mx-2">
|
| 60 |
+
<!-- Carousel will be inserted here -->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
</div>
|
| 62 |
</div>
|
| 63 |
</section>
|
| 64 |
|
| 65 |
+
<!-- Research Areas Section - Transparent Card with 2x2 Grid -->
|
| 66 |
+
<section id="research-areas" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 67 |
+
<div class="bg-white/20 backdrop-blur-sm shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 68 |
+
<h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-8 text-center">Research Areas</h2>
|
| 69 |
+
<div id="research-areas-grid" class="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6">
|
| 70 |
+
<!-- Area cards will be inserted here by JavaScript -->
|
| 71 |
+
</div>
|
| 72 |
+
</div>
|
| 73 |
+
</section>
|
| 74 |
|
| 75 |
+
<!-- Team Members Section - Frosted Background -->
|
| 76 |
+
<section id="team" class="mb-16 relative z-20 px-4 sm:px-6 lg:px-8">
|
| 77 |
+
<div class="bg-white/40 backdrop-blur-sm shadow-sm rounded-lg p-6 md:p-10 w-full" style="max-width: min(90%, 1400px); margin: 0 auto;">
|
| 78 |
+
<h2 class="text-3xl md:text-4xl font-bold text-gray-900 mb-6">Team Members</h2>
|
| 79 |
+
<div id="team-grid" class="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 mb-6">
|
| 80 |
+
<!-- Team members will be inserted here by JavaScript -->
|
| 81 |
+
</div>
|
| 82 |
+
<div class="prose max-w-none text-gray-700 pt-4 border-t border-gray-200">
|
| 83 |
+
<p>We also work closely with
|
| 84 |
+
<a href="https://huggingface.co/irenesolaiman" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Irene Solaiman</a> (Chief Policy Officer),
|
| 85 |
+
<a href="https://huggingface.co/evijit" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Avijit Ghosh</a> (Applied Policy Researcher) in the policy team,
|
| 86 |
+
and with <a href="https://huggingface.co/meg" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Meg Mitchell</a> (Chief Ethics Scientist),
|
| 87 |
+
and with <a href="https://huggingface.co/brunatrevelin" class="text-blue-600 hover:text-blue-800 transition-colors" target="_blank">Bruna Trevelin</a> (Legal Counsel)!
|
| 88 |
+
</p>
|
| 89 |
+
</div>
|
| 90 |
+
</div>
|
| 91 |
</section>
|
| 92 |
`;
|
| 93 |
|
|
|
|
| 102 |
};
|
| 103 |
}
|
| 104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
function initializeTeamMembers() {
|
| 106 |
const teamContainer = document.getElementById('team-grid');
|
| 107 |
if (!teamContainer) {
|
|
|
|
| 124 |
}
|
| 125 |
|
| 126 |
function initializeHomeAreaCards() {
|
| 127 |
+
const areasContainer = document.getElementById('research-areas-grid');
|
| 128 |
if (!areasContainer) return;
|
| 129 |
|
| 130 |
const areas = Object.values(areasData);
|
| 131 |
+
areasContainer.innerHTML = areas.map(area => createResearchAreaCard(area)).join('');
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
function createResearchAreaCard(area) {
|
| 135 |
+
// Get first sentence of description
|
| 136 |
+
const shortDesc = area.description.split('.')[0] + '.';
|
| 137 |
+
|
| 138 |
+
// Get topic names and colors from the subAreas - same logic as ArtifactSummaryCard
|
| 139 |
+
const topics = Object.values(area.subAreas).map(subArea => {
|
| 140 |
+
const topicName = subArea.navName || subArea.name;
|
| 141 |
+
let bgColor = 'bg-gray-200';
|
| 142 |
+
let textColor = 'text-gray-700';
|
| 143 |
+
|
| 144 |
+
// Extract background and text color from the subArea color class
|
| 145 |
+
if (subArea.color) {
|
| 146 |
+
// Match pattern like "bg-emerald-100 text-emerald-800"
|
| 147 |
+
const bgMatch = subArea.color.match(/bg-(\w+)-(\d+)/);
|
| 148 |
+
const textMatch = subArea.color.match(/text-(\w+)-(\d+)/);
|
| 149 |
+
|
| 150 |
+
if (bgMatch) {
|
| 151 |
+
bgColor = `bg-${bgMatch[1]}-${bgMatch[2]}`;
|
| 152 |
+
}
|
| 153 |
+
if (textMatch) {
|
| 154 |
+
textColor = `text-${textMatch[1]}-700`; // Use consistent 700 shade like artifact cards
|
| 155 |
+
}
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
return {
|
| 159 |
+
name: topicName,
|
| 160 |
+
bgColor,
|
| 161 |
+
textColor
|
| 162 |
+
};
|
| 163 |
+
});
|
| 164 |
+
|
| 165 |
+
return `
|
| 166 |
+
<a href="/${area.id}"
|
| 167 |
+
class="group relative block border border-gray-200 rounded-lg overflow-hidden bg-white hover:shadow-lg transition-all duration-200 h-64">
|
| 168 |
+
|
| 169 |
+
<!-- Background image with low opacity -->
|
| 170 |
+
${area.image ? `
|
| 171 |
+
<div class="absolute inset-0 opacity-15 group-hover:opacity-30 transition-opacity">
|
| 172 |
+
<img src="/images/${area.image}" alt="" class="w-full h-full object-cover">
|
| 173 |
+
</div>
|
| 174 |
+
` : ''}
|
| 175 |
+
|
| 176 |
+
<!-- Content -->
|
| 177 |
+
<div class="relative p-5 h-full flex flex-col">
|
| 178 |
+
<!-- Header -->
|
| 179 |
+
<div class="flex justify-between items-start mb-3 flex-shrink-0">
|
| 180 |
+
<h3 class="text-lg font-bold text-gray-900 leading-tight">${area.navTitle}</h3>
|
| 181 |
+
<svg class="w-5 h-5 text-gray-400 group-hover:text-blue-600 transition-colors flex-shrink-0 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
| 182 |
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
|
| 183 |
+
</svg>
|
| 184 |
+
</div>
|
| 185 |
+
|
| 186 |
+
<!-- Description - scrollable -->
|
| 187 |
+
<div class="text-md text-gray-700 mb-4 flex-grow overflow-y-auto pr-2">
|
| 188 |
+
<p class="leading-relaxed">${area.description}</p>
|
| 189 |
+
</div>
|
| 190 |
+
|
| 191 |
+
<!-- Topics with colors from subAreas - same style as artifact cards -->
|
| 192 |
+
<div class="flex-shrink-0">
|
| 193 |
+
<p class="text-xs font-semibold text-gray-600 uppercase tracking-wide mb-2">Topics:</p>
|
| 194 |
+
<div class="flex flex-wrap gap-2">
|
| 195 |
+
${topics.map(topic => `
|
| 196 |
+
<span class="inline-block px-2 py-0.5 text-sm ${topic.bgColor} ${topic.textColor} rounded">
|
| 197 |
+
${topic.name}
|
| 198 |
+
</span>
|
| 199 |
+
`).join('')}
|
| 200 |
+
</div>
|
| 201 |
+
</div>
|
| 202 |
+
|
| 203 |
+
<!-- Image Attribution -->
|
| 204 |
+
${area.imageAttribution ? `
|
| 205 |
+
<p class="text-xs text-gray-500 mt-3 pt-2 border-t border-gray-100 flex-shrink-0">${area.imageAttribution}</p>
|
| 206 |
+
` : ''}
|
| 207 |
+
</div>
|
| 208 |
+
</a>
|
| 209 |
+
`;
|
| 210 |
}
|
| 211 |
|
| 212 |
function initializeArtifactCarousels() {
|
js/pages/ResourcesPage.js
CHANGED
|
@@ -3,7 +3,6 @@ import { homeBackgroundImage, areasData } from '../data/areas.js';
|
|
| 3 |
import { pressMentions } from '../data/press.js';
|
| 4 |
import { sampleResources } from '../data/resources.js';
|
| 5 |
import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
|
| 6 |
-
import { renderSidebar } from '../utils/sidebar.js'; // Import the new sidebar component
|
| 7 |
|
| 8 |
export function renderResourcesPage() {
|
| 9 |
const content = `
|
|
@@ -107,7 +106,7 @@ export function renderResourcesPage() {
|
|
| 107 |
<div class="bg-gray-50 rounded-lg p-4 ${borderSide} ${borderColor} relative overflow-hidden">
|
| 108 |
<!-- Background image for this article -->
|
| 109 |
<div class="absolute inset-0 opacity-5 pointer-events-none">
|
| 110 |
-
<img src="images/${primaryArea?.image || 'ai.png'}" alt="" class="w-full h-full object-cover">
|
| 111 |
</div>
|
| 112 |
|
| 113 |
<div class="relative z-10">
|
|
@@ -156,25 +155,6 @@ export function renderResourcesPage() {
|
|
| 156 |
};
|
| 157 |
}
|
| 158 |
|
| 159 |
-
export function getResourcesPageSidebar() {
|
| 160 |
-
const sidebarItems = [
|
| 161 |
-
{
|
| 162 |
-
label: 'Technical Resources',
|
| 163 |
-
href: '#technical-resources',
|
| 164 |
-
isNested: false,
|
| 165 |
-
isActive: window.location.hash === '#technical-resources',
|
| 166 |
-
},
|
| 167 |
-
{
|
| 168 |
-
label: 'Press Mentions',
|
| 169 |
-
href: '#press-mentions',
|
| 170 |
-
isNested: false,
|
| 171 |
-
isActive: window.location.hash === '#press-mentions',
|
| 172 |
-
},
|
| 173 |
-
];
|
| 174 |
-
|
| 175 |
-
return renderSidebar('Resources', sidebarItems);
|
| 176 |
-
}
|
| 177 |
-
|
| 178 |
function initializeResourcesBackgroundAttribution() {
|
| 179 |
const backgroundContainer = document.getElementById('resources-background-container');
|
| 180 |
const attribution = document.getElementById('resources-bg-attribution');
|
|
|
|
| 3 |
import { pressMentions } from '../data/press.js';
|
| 4 |
import { sampleResources } from '../data/resources.js';
|
| 5 |
import { createResourceCard, initializeResourceCards } from '../cards/ResourceCard.js';
|
|
|
|
| 6 |
|
| 7 |
export function renderResourcesPage() {
|
| 8 |
const content = `
|
|
|
|
| 106 |
<div class="bg-gray-50 rounded-lg p-4 ${borderSide} ${borderColor} relative overflow-hidden">
|
| 107 |
<!-- Background image for this article -->
|
| 108 |
<div class="absolute inset-0 opacity-5 pointer-events-none">
|
| 109 |
+
<img src="/images/${primaryArea?.image || 'ai.png'}" alt="" class="w-full h-full object-cover">
|
| 110 |
</div>
|
| 111 |
|
| 112 |
<div class="relative z-10">
|
|
|
|
| 155 |
};
|
| 156 |
}
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
function initializeResourcesBackgroundAttribution() {
|
| 159 |
const backgroundContainer = document.getElementById('resources-background-container');
|
| 160 |
const attribution = document.getElementById('resources-bg-attribution');
|