let documents = []; let currentDocument = null; let documentCards = null; let currentLanguage = 'en'; // Default language // UI translations for full page i18n const UI_TRANSLATIONS = { en: { pageTitle: 'Framework Documentation', pageSubtitle: 'Technical specifications, guides, and reference materials', documentsHeading: 'Documents', searchButton: 'Search', backToDocuments: 'Back to Documents', selectDocument: 'Select a Document', selectDocumentDesc: 'Choose a document from the sidebar to begin reading', loadingDocument: 'Loading document...', errorLoadingDoc: 'Error loading document', tableOfContents: 'Table of Contents', downloadPdf: 'Download PDF', github: 'GitHub', publicRepository: 'Public Repository', publicRepositoryDesc: 'Source code, examples & contributions', readmeQuickStart: 'README & Quick Start', readmeQuickStartDesc: 'Installation and getting started guide', categories: { 'getting-started': 'Getting Started', 'resources': 'Resources', 'research-theory': 'Research & Theory', 'technical-reference': 'Technical Reference', 'advanced-topics': 'Advanced Topics', 'business-leadership': 'Business & Leadership' } }, de: { pageTitle: 'Framework-Dokumentation', pageSubtitle: 'Technische Spezifikationen, Leitfäden und Referenzmaterialien', documentsHeading: 'Dokumente', searchButton: 'Suchen', backToDocuments: 'Zurück zu Dokumenten', selectDocument: 'Dokument auswählen', selectDocumentDesc: 'Wählen Sie ein Dokument aus der Seitenleiste, um mit dem Lesen zu beginnen', loadingDocument: 'Dokument wird geladen...', errorLoadingDoc: 'Fehler beim Laden des Dokuments', tableOfContents: 'Inhaltsverzeichnis', downloadPdf: 'PDF herunterladen', github: 'GitHub', publicRepository: 'Öffentliches Repository', publicRepositoryDesc: 'Quellcode, Beispiele und Beiträge', readmeQuickStart: 'README & Schnellstart', readmeQuickStartDesc: 'Installation und Einstiegsanleitung', categories: { 'getting-started': 'Erste Schritte', 'resources': 'Ressourcen', 'research-theory': 'Forschung & Theorie', 'technical-reference': 'Technische Referenz', 'advanced-topics': 'Fortgeschrittene Themen', 'business-leadership': 'Business & Führung' } }, fr: { pageTitle: 'Documentation du Framework', pageSubtitle: 'Spécifications techniques, guides et matériels de référence', documentsHeading: 'Documents', searchButton: 'Rechercher', backToDocuments: 'Retour aux documents', selectDocument: 'Sélectionner un document', selectDocumentDesc: 'Choisissez un document dans la barre latérale pour commencer la lecture', loadingDocument: 'Chargement du document...', errorLoadingDoc: 'Erreur lors du chargement du document', tableOfContents: 'Table des matières', downloadPdf: 'Télécharger PDF', github: 'GitHub', publicRepository: 'Dépôt public', publicRepositoryDesc: 'Code source, exemples et contributions', readmeQuickStart: 'README & Démarrage rapide', readmeQuickStartDesc: 'Guide d\'installation et de démarrage', categories: { 'getting-started': 'Premiers pas', 'resources': 'Ressources', 'research-theory': 'Recherche & Théorie', 'technical-reference': 'Référence technique', 'advanced-topics': 'Sujets avancés', 'business-leadership': 'Business & Leadership' } } }; // Get current UI translations function getUITranslations(lang = currentLanguage) { return UI_TRANSLATIONS[lang] || UI_TRANSLATIONS.en; } // Update all page UI elements with current language translations function updatePageUI(lang = currentLanguage) { const t = getUITranslations(lang); // Update page header const pageTitle = document.querySelector('h1'); if (pageTitle && pageTitle.textContent === UI_TRANSLATIONS.en.pageTitle || pageTitle.textContent === UI_TRANSLATIONS.de.pageTitle || pageTitle.textContent === UI_TRANSLATIONS.fr.pageTitle) { pageTitle.textContent = t.pageTitle; } const pageSubtitle = document.querySelector('.text-gray-600.mt-2'); if (pageSubtitle && (pageSubtitle.textContent === UI_TRANSLATIONS.en.pageSubtitle || pageSubtitle.textContent === UI_TRANSLATIONS.de.pageSubtitle || pageSubtitle.textContent === UI_TRANSLATIONS.fr.pageSubtitle)) { pageSubtitle.textContent = t.pageSubtitle; } // Update search button const searchBtn = document.querySelector('#open-search-modal-btn span'); if (searchBtn) { searchBtn.textContent = t.searchButton; } // Update sidebar Documents heading const docsHeading = document.querySelector('aside h3'); if (docsHeading && (docsHeading.textContent === UI_TRANSLATIONS.en.documentsHeading || docsHeading.textContent === UI_TRANSLATIONS.de.documentsHeading || docsHeading.textContent === UI_TRANSLATIONS.fr.documentsHeading)) { docsHeading.textContent = t.documentsHeading; } // Update GitHub section heading const githubHeadings = document.querySelectorAll('aside h3'); githubHeadings.forEach(heading => { if (heading.textContent.trim() === UI_TRANSLATIONS.en.github || heading.textContent.trim() === UI_TRANSLATIONS.de.github || heading.textContent.trim() === UI_TRANSLATIONS.fr.github) { // Keep the SVG icon, just update text const textNode = Array.from(heading.childNodes).find(node => node.nodeType === Node.TEXT_NODE); if (textNode) { textNode.textContent = t.github; } } }); // Update GitHub links const githubLinks = document.querySelectorAll('aside a[href*="github.com"]'); githubLinks.forEach(link => { const titleDiv = link.querySelector('.text-sm.font-medium'); const descDiv = link.querySelector('.text-xs.text-gray-500'); if (titleDiv) { if (titleDiv.textContent === UI_TRANSLATIONS.en.publicRepository || titleDiv.textContent === UI_TRANSLATIONS.de.publicRepository || titleDiv.textContent === UI_TRANSLATIONS.fr.publicRepository) { titleDiv.textContent = t.publicRepository; } else if (titleDiv.textContent === UI_TRANSLATIONS.en.readmeQuickStart || titleDiv.textContent === UI_TRANSLATIONS.de.readmeQuickStart || titleDiv.textContent === UI_TRANSLATIONS.fr.readmeQuickStart) { titleDiv.textContent = t.readmeQuickStart; } } if (descDiv) { if (descDiv.textContent === UI_TRANSLATIONS.en.publicRepositoryDesc || descDiv.textContent === UI_TRANSLATIONS.de.publicRepositoryDesc || descDiv.textContent === UI_TRANSLATIONS.fr.publicRepositoryDesc) { descDiv.textContent = t.publicRepositoryDesc; } else if (descDiv.textContent === UI_TRANSLATIONS.en.readmeQuickStartDesc || descDiv.textContent === UI_TRANSLATIONS.de.readmeQuickStartDesc || descDiv.textContent === UI_TRANSLATIONS.fr.readmeQuickStartDesc) { descDiv.textContent = t.readmeQuickStartDesc; } } }); // Update Back to Documents button const backBtn = document.querySelector('#back-to-docs-btn span'); if (backBtn) { backBtn.textContent = t.backToDocuments; } // Update Select Document placeholder (if visible) const selectDocHeading = document.getElementById('select-document-heading'); if (selectDocHeading) { selectDocHeading.textContent = t.selectDocument; } const selectDocDesc = document.getElementById('select-document-desc'); if (selectDocDesc) { selectDocDesc.textContent = t.selectDocumentDesc; } // Update page title tag document.title = `${t.pageTitle} | Tractatus AI Safety`; } // Initialize card-based viewer if (typeof DocumentCards !== 'undefined') { documentCards = new DocumentCards('document-content'); } // Detect language from URL, localStorage, or i18n system function detectLanguage() { // Priority 1: URL parameter const urlParams = new URLSearchParams(window.location.search); const urlLang = urlParams.get('lang'); if (urlLang) { return urlLang; } // Priority 2: localStorage const storedLang = localStorage.getItem('tractatus-lang'); if (storedLang) { return storedLang; } // Priority 3: i18n system if (window.I18n && window.I18n.currentLang) { return window.I18n.currentLang; } // Default: English return 'en'; } // Update URL with language parameter function updateURL(slug, lang) { const url = new URL(window.location); url.searchParams.set('doc', slug); if (lang && lang !== 'en') { url.searchParams.set('lang', lang); } else { url.searchParams.delete('lang'); } window.history.pushState({}, '', url); } // Listen for language changes from i18n system if (typeof window !== 'undefined') { window.addEventListener('languageChanged', async (e) => { const newLang = e.detail.language; currentLanguage = newLang; // Update page UI (hero section, sidebar headings, etc.) updatePageUI(newLang); // Remember current document slug before reloading list const currentSlug = currentDocument ? currentDocument.slug : null; // Update URL lang parameter BEFORE reloading documents // This ensures detectLanguage() reads the correct language from URL if (currentSlug) { updateURL(currentSlug, newLang); } // Reload document list to show translated category labels and document titles await loadDocuments(); // Explicitly reload current document to ensure it updates // (loadDocuments auto-loads from URL, but explicit call ensures it happens) if (currentSlug) { await loadDocument(currentSlug, newLang); } }); // Initialize language on i18n ready window.addEventListener('i18nInitialized', (e) => { currentLanguage = e.detail.language; }); } // Document categorization - Final 5 categories (curated for public docs) const CATEGORIES = { 'getting-started': { icon: '📚', description: 'Introduction, core concepts, and glossary', order: 1, color: 'blue', bgColor: 'bg-blue-50', borderColor: 'border-l-4 border-blue-500', textColor: 'text-blue-700', collapsed: false }, 'resources': { icon: '📖', description: 'Implementation guides and reference materials', order: 2, color: 'amber', bgColor: 'bg-amber-50', borderColor: 'border-l-4 border-amber-500', textColor: 'text-amber-700', collapsed: false }, 'research-theory': { icon: '🔬', description: 'Research papers, case studies, theoretical foundations', order: 3, color: 'purple', bgColor: 'bg-purple-50', borderColor: 'border-l-4 border-purple-500', textColor: 'text-purple-700', collapsed: true }, 'technical-reference': { icon: '🔌', description: 'API documentation, code examples, architecture', order: 4, color: 'green', bgColor: 'bg-green-50', borderColor: 'border-l-4 border-green-500', textColor: 'text-green-700', collapsed: true }, 'advanced-topics': { icon: '🎓', description: 'Value pluralism, organizational theory, advanced concepts', order: 5, color: 'teal', bgColor: 'bg-teal-50', borderColor: 'border-l-4 border-teal-500', textColor: 'text-teal-700', collapsed: true }, 'business-leadership': { icon: '💼', description: 'Business cases, ROI analysis, executive briefs', order: 6, color: 'pink', bgColor: 'bg-pink-50', borderColor: 'border-l-4 border-pink-500', textColor: 'text-pink-700', collapsed: true } }; // Documents to hide (internal/confidential) const HIDDEN_DOCS = [ 'security-audit-report', 'koha-production-deployment', 'koha-stripe-payment', 'appendix-e-contact', 'cover-letter' ]; // Categorize a document using database category field // New granular category system for better document organization function categorizeDocument(doc) { const slug = doc.slug.toLowerCase(); // Skip hidden documents if (HIDDEN_DOCS.some(hidden => slug.includes(hidden))) { return null; } // Use category from database const category = doc.category || 'resources'; // Validate category exists in CATEGORIES constant if (CATEGORIES[category]) { return category; } // Fallback to resources for uncategorized console.warn(`Document "${doc.title}" has invalid category "${category}", using fallback to resources`); return 'resources'; } // Group documents by category function groupDocuments(docs) { const grouped = {}; // Initialize all categories Object.keys(CATEGORIES).forEach(key => { grouped[key] = []; }); // Categorize each document (already sorted by order from API) docs.forEach(doc => { const category = categorizeDocument(doc); if (category && grouped[category]) { grouped[category].push(doc); } }); return grouped; } // Render document link with download button function renderDocLink(doc, isHighlighted = false) { const highlightClass = isHighlighted ? 'text-blue-700 bg-blue-50 border border-blue-200' : ''; // Get translated title if available and language is not English let displayTitle = doc.title; if (currentLanguage !== 'en' && doc.translations && doc.translations[currentLanguage]) { displayTitle = doc.translations[currentLanguage].title || doc.title; } // Determine if PDF download is available and get PDF path // First check if document has explicit download_formats.pdf let pdfPath = null; let hasPDF = false; if (doc.download_formats && doc.download_formats.pdf) { pdfPath = doc.download_formats.pdf; hasPDF = true; } else if (!doc.slug.includes('api-reference-complete') && !doc.slug.includes('openapi-specification') && !doc.slug.includes('api-javascript-examples') && !doc.slug.includes('api-python-examples') && !doc.slug.includes('technical-architecture-diagram')) { // Fallback to default /downloads/ path for documents that typically have PDFs pdfPath = `/downloads/${doc.slug}.pdf`; hasPDF = true; } // Add download button styling const paddingClass = hasPDF ? 'pr-10' : 'pr-3'; return `
`; } // Load document list async function loadDocuments() { try { // Fetch public documents const response = await fetch('/api/documents'); const data = await response.json(); documents = data.documents || []; const listEl = document.getElementById('document-list'); if (documents.length === 0) { listEl.innerHTML = '${t.loadingDocument}
${error.message}