// Theme management const themeToggle = document.getElementById('themeToggle'); const html = document.documentElement; // Load saved theme const savedTheme = localStorage.getItem('theme') || 'light'; html.setAttribute('data-theme', savedTheme); themeToggle.addEventListener('click', () => { const currentTheme = html.getAttribute('data-theme'); const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; html.setAttribute('data-theme', newTheme); localStorage.setItem('theme', newTheme); }); // Sidebar reference (no collapse functionality) const sidebar = document.getElementById('sidebar'); // Sidebar resizing const resizeHandle = document.getElementById('resizeHandle'); let isResizing = false; let startX = 0; let startWidth = 0; resizeHandle.addEventListener('mousedown', (e) => { isResizing = true; startX = e.clientX; startWidth = parseInt(window.getComputedStyle(sidebar).width, 10); document.addEventListener('mousemove', handleResize); document.addEventListener('mouseup', stopResize); e.preventDefault(); }); function handleResize(e) { if (!isResizing) return; const width = startWidth + e.clientX - startX; const minWidth = 200; const maxWidth = 600; if (width >= minWidth && width <= maxWidth) { sidebar.style.width = `${width}px`; document.documentElement.style.setProperty('--sidebar-width', `${width}px`); } } function stopResize() { isResizing = false; document.removeEventListener('mousemove', handleResize); document.removeEventListener('mouseup', stopResize); } // Tab switching const navTabs = document.querySelectorAll('.nav-tab'); const docsTab = document.getElementById('docsTab'); const courseTab = document.getElementById('courseTab'); navTabs.forEach(tab => { tab.addEventListener('click', () => { const tabName = tab.dataset.tab; // Update active tab navTabs.forEach(t => t.classList.remove('active')); tab.classList.add('active'); // Show/hide tab content if (tabName === 'docs') { docsTab.style.display = 'block'; courseTab.style.display = 'none'; } else { docsTab.style.display = 'none'; courseTab.style.display = 'block'; loadCourseContent(); } }); }); // API interaction const objectInput = document.getElementById('objectInput'); const langSelect = document.getElementById('langSelect'); const searchBtn = document.getElementById('searchBtn'); const resultsContainer = document.getElementById('results'); const moduleListContainer = document.getElementById('moduleList'); const courseListContainer = document.getElementById('courseList'); // Current state let currentObject = ''; let currentLang = ''; // Load module list async function loadModuleList() { try { const response = await fetch('/modules'); const data = await response.json(); const list = document.createElement('div'); list.className = 'module-items'; // Add builtins section if (data.builtins && data.builtins.length > 0) { const builtinsSection = document.createElement('div'); builtinsSection.className = 'module-section'; const builtinsTitle = document.createElement('h4'); builtinsTitle.textContent = 'Builtins'; builtinsTitle.className = 'module-section-title'; builtinsSection.appendChild(builtinsTitle); const builtinsList = document.createElement('div'); builtinsList.className = 'module-items-list'; data.builtins.slice(0, 20).forEach(item => { const btn = createModuleButton(item.full_name || `builtins.${item.name}`, item.name); builtinsList.appendChild(btn); }); builtinsSection.appendChild(builtinsList); list.appendChild(builtinsSection); } // Add modules section if (data.modules && data.modules.length > 0) { const modulesSection = document.createElement('div'); modulesSection.className = 'module-section'; const modulesTitle = document.createElement('h4'); modulesTitle.textContent = 'Standard Library'; modulesTitle.className = 'module-section-title'; modulesSection.appendChild(modulesTitle); const modulesList = document.createElement('div'); modulesList.className = 'module-items-list'; data.modules.forEach(item => { const btn = createModuleButton(item.name, item.name); modulesList.appendChild(btn); }); modulesSection.appendChild(modulesList); list.appendChild(modulesSection); } moduleListContainer.innerHTML = ''; moduleListContainer.appendChild(list); } catch (error) { moduleListContainer.innerHTML = `
Error loading modules: ${error.message}
`; } } function createModuleButton(fullName, displayName) { const btn = document.createElement('button'); btn.className = 'module-item'; btn.textContent = displayName; btn.title = fullName; btn.addEventListener('click', () => { objectInput.value = fullName; currentObject = fullName; fetchDocumentation(); }); return btn; } // Load modules on page load loadModuleList(); // Auto-translate when language changes langSelect.addEventListener('change', () => { if (currentObject) { currentLang = langSelect.value; fetchDocumentation(); } }); async function fetchDocumentation() { const objectName = objectInput.value.trim(); const targetLang = langSelect.value; if (!objectName) { resultsContainer.innerHTML = '
Please enter a Python object name.
'; return; } currentObject = objectName; currentLang = targetLang; searchBtn.disabled = true; searchBtn.textContent = 'Loading...'; resultsContainer.innerHTML = '
Loading documentation...
'; try { const params = new URLSearchParams({ object: objectName }); if (targetLang) { params.append('lang', targetLang); } const response = await fetch(`/docs?${params}`); const data = await response.json(); if (data.error) { resultsContainer.innerHTML = `
Error: ${data.error}
`; return; } displayResults(data, targetLang); } catch (error) { resultsContainer.innerHTML = `
Network error: ${error.message}
`; } finally { searchBtn.disabled = false; searchBtn.textContent = 'Get Documentation'; } } function displayResults(data, targetLang) { // Python docs style - no card, clean layout const wrapper = document.createElement('div'); // Header with title const header = document.createElement('div'); header.className = 'doc-header'; const title = document.createElement('h1'); title.className = 'doc-title'; title.textContent = data.object_name; const meta = document.createElement('div'); meta.className = 'doc-meta'; meta.innerHTML = ` ${data.object_type || 'unknown'} ${data.cached ? 'Cached' : ''} `; header.appendChild(title); header.appendChild(meta); wrapper.appendChild(header); // Signature if (data.signature) { const signature = document.createElement('div'); signature.className = 'doc-signature'; signature.textContent = data.signature; wrapper.appendChild(signature); } // Main documentation content const docText = data.translated || data.original; if (docText) { const docSection = document.createElement('section'); docSection.className = 'doc-section'; const docTextEl = document.createElement('div'); docTextEl.className = 'doc-text'; docTextEl.textContent = docText; docSection.appendChild(docTextEl); wrapper.appendChild(docSection); } // Show original if translation exists (collapsible) if (data.translated && data.original) { const originalSection = document.createElement('details'); originalSection.className = 'doc-section'; originalSection.innerHTML = ` Original Documentation (English)
${data.original}
`; wrapper.appendChild(originalSection); } if (!data.original && !data.translated) { const noDoc = document.createElement('div'); noDoc.className = 'doc-text'; noDoc.textContent = 'No documentation available for this object.'; wrapper.appendChild(noDoc); } resultsContainer.innerHTML = ''; resultsContainer.appendChild(wrapper); } // Load course content async function loadCourseContent() { if (courseListContainer.querySelector('.course-loaded')) { return; // Already loaded } try { const response = await fetch('/course'); const data = await response.json(); const list = document.createElement('div'); list.className = 'module-items'; list.classList.add('course-loaded'); if (data.sections && data.sections.length > 0) { data.sections.forEach(section => { const sectionDiv = document.createElement('div'); sectionDiv.className = 'module-section'; const sectionTitle = document.createElement('h4'); sectionTitle.textContent = section.title; sectionTitle.className = 'module-section-title'; sectionDiv.appendChild(sectionTitle); const itemsList = document.createElement('div'); itemsList.className = 'module-items-list'; // Create clickable navigation item const navBtn = document.createElement('button'); navBtn.className = 'module-item'; navBtn.textContent = section.title; navBtn.addEventListener('click', () => { // Scroll to section in main content const sectionId = section.id || section.title.toLowerCase().replace(/\s+/g, '-'); const sectionElement = document.getElementById(`section-${sectionId}`); if (sectionElement) { sectionElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); } else { // Try to find by heading ID const headingId = section.title.toLowerCase() .replace(/[^\w\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .trim(); const headingElement = document.getElementById(headingId); if (headingElement) { headingElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); } } }); itemsList.appendChild(navBtn); // Add subsections if any if (section.subsections && section.subsections.length > 0) { section.subsections.forEach(subsection => { const subBtn = document.createElement('button'); subBtn.className = 'module-item'; subBtn.style.paddingLeft = '1.5rem'; subBtn.textContent = subsection.title; subBtn.addEventListener('click', () => { const subId = subsection.id || subsection.title.toLowerCase().replace(/\s+/g, '-'); const subElement = document.getElementById(`subsection-${subId}`); if (subElement) { subElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); itemsList.appendChild(subBtn); }); } sectionDiv.appendChild(itemsList); list.appendChild(sectionDiv); }); } courseListContainer.innerHTML = ''; courseListContainer.appendChild(list); // Also display course content in main area displayCourseContent(data); } catch (error) { courseListContainer.innerHTML = `
Error loading course: ${error.message}
`; } } function displayCourseContent(courseData) { const wrapper = document.createElement('div'); wrapper.className = 'course-content'; const title = document.createElement('h1'); title.textContent = courseData.title || 'Python Course'; wrapper.appendChild(title); if (courseData.sections && courseData.sections.length > 0) { courseData.sections.forEach(section => { const sectionDiv = document.createElement('section'); sectionDiv.className = 'course-section'; sectionDiv.id = `section-${section.id || section.title.toLowerCase().replace(/\s+/g, '-')}`; // Parse and render markdown if (section.markdown) { // Configure marked options marked.setOptions({ highlight: function(code, lang) { if (lang && hljs.getLanguage(lang)) { try { return hljs.highlight(code, { language: lang }).value; } catch (err) { console.error('Highlight error:', err); } } return hljs.highlightAuto(code).value; }, breaks: true, gfm: true }); // Convert markdown to HTML const htmlContent = marked.parse(section.markdown); // Create a container for the markdown content const contentDiv = document.createElement('div'); contentDiv.className = 'markdown-content'; contentDiv.innerHTML = htmlContent; // Add IDs to headings for navigation contentDiv.querySelectorAll('h1, h2, h3, h4').forEach((heading) => { const text = heading.textContent.trim(); const id = text.toLowerCase() .replace(/[^\w\s-]/g, '') .replace(/\s+/g, '-') .replace(/-+/g, '-') .trim(); heading.id = id; }); // Highlight code blocks contentDiv.querySelectorAll('pre code').forEach((block) => { hljs.highlightElement(block); }); sectionDiv.appendChild(contentDiv); } else if (section.content && section.content.length > 0) { // Fallback to old format section.content.forEach(item => { if (item.startsWith('```')) { const codeDiv = document.createElement('pre'); codeDiv.className = 'doc-signature'; codeDiv.textContent = item.replace(/```python\n?/g, '').replace(/```/g, '').trim(); sectionDiv.appendChild(codeDiv); } else { const itemDiv = document.createElement('p'); itemDiv.className = 'doc-text'; itemDiv.textContent = item; sectionDiv.appendChild(itemDiv); } }); } wrapper.appendChild(sectionDiv); }); } resultsContainer.innerHTML = ''; resultsContainer.appendChild(wrapper); } function getLanguageName(langCode) { const langMap = { 'de': 'German', 'fr': 'French', 'es': 'Spanish', 'it': 'Italian', 'pt': 'Portuguese', 'ru': 'Russian' }; return langMap[langCode] || langCode; } searchBtn.addEventListener('click', fetchDocumentation); objectInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { fetchDocumentation(); } });