From d8e50618739aad7b44275b711989c3bca512bbaa Mon Sep 17 00:00:00 2001 From: TheFlow Date: Sun, 19 Oct 2025 12:41:48 +1300 Subject: [PATCH] fix(mobile): implement navigation toggle for document viewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add mobile-specific navigation pattern to resolve catch-22 UX issue where users couldn't see documents without scrolling but didn't know to scroll. Changes: - Add mobile CSS to toggle between sidebar and document viewer - Add back button to return to document list on mobile - Add document-active body class to manage navigation state - Update GitHub repository links to correct URL 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- public/docs.html | 67 +++++++++++++++++++++++----- public/js/docs-app.js | 101 +++++++++++++++++++++++++++++++++--------- 2 files changed, 137 insertions(+), 31 deletions(-) diff --git a/public/docs.html b/public/docs.html index 4794a0cb..320aab41 100644 --- a/public/docs.html +++ b/public/docs.html @@ -3,8 +3,11 @@ + + + Framework Documentation | Tractatus AI Safety - + @@ -14,9 +17,11 @@ - + + + @@ -539,7 +576,7 @@ GitHub - @@ -569,7 +606,17 @@ -
+
+ + +
@@ -811,13 +858,13 @@ - - - + + + - - + + diff --git a/public/js/docs-app.js b/public/js/docs-app.js index 826e42d3..9b5ae47b 100644 --- a/public/js/docs-app.js +++ b/public/js/docs-app.js @@ -133,15 +133,23 @@ function groupDocuments(docs) { function renderDocLink(doc, isHighlighted = false) { const highlightClass = isHighlighted ? 'text-blue-700 bg-blue-50 border border-blue-200' : ''; - // Determine if PDF download is available - // Check if metadata indicates PDF existence (presence in slugs that typically have PDFs) - // Documents with download_formats.pdf, markdown files, or most docs should have PDFs - // API Reference docs and technical diagrams might not have PDFs - const hasPDF = !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'); + // 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'; @@ -153,7 +161,7 @@ function renderDocLink(doc, isHighlighted = false) {
${doc.title}
${hasPDF ? ` -
0) { + // Priority 1: Load specific document by slug if provided + if (docParam) { + const doc = documents.find(d => d.slug === docParam); + if (doc) { + // Find and expand the category containing this document + const docCategory = categorizeDocument(doc); + if (docCategory) { + const categoryDocsEl = listEl.querySelector(`.category-docs[data-category="${docCategory}"]`); + const categoryArrowEl = listEl.querySelector(`.category-toggle[data-category="${docCategory}"] .category-arrow`); + + if (categoryDocsEl) { + categoryDocsEl.style.display = 'block'; + if (categoryArrowEl) { + categoryArrowEl.style.transform = 'rotate(0deg)'; + } + } + } + + // Load the requested document + loadDocument(docParam); + } else { + console.warn(`Document with slug "${docParam}" not found`); + } + } + // Priority 2: Load category if provided but no specific document + else if (categoryParam && grouped[categoryParam] && grouped[categoryParam].length > 0) { // Expand the specified category const categoryDocsEl = listEl.querySelector(`.category-docs[data-category="${categoryParam}"]`); const categoryArrowEl = listEl.querySelector(`.category-toggle[data-category="${categoryParam}"] .category-arrow`); @@ -330,7 +363,9 @@ async function loadDocuments() { if (firstDoc) { loadDocument(firstDoc.slug); } - } else { + } + // Priority 3: Default behavior + else { // Default: Auto-load first document in "Getting Started" category (order: 1) const gettingStartedDocs = grouped['getting-started'] || []; if (gettingStartedDocs.length > 0) { @@ -403,12 +438,21 @@ async function loadDocument(slug) { // Fallback to traditional view with header const hasToC = currentDocument.toc && currentDocument.toc.length > 0; - // Check if PDF is available (same logic as sidebar) - const hasPDF = !currentDocument.slug.includes('api-reference-complete') && - !currentDocument.slug.includes('openapi-specification') && - !currentDocument.slug.includes('api-javascript-examples') && - !currentDocument.slug.includes('api-python-examples') && - !currentDocument.slug.includes('technical-architecture-diagram'); + // Check if PDF is available and get PDF path + let pdfPath = null; + let hasPDF = false; + + if (currentDocument.download_formats && currentDocument.download_formats.pdf) { + pdfPath = currentDocument.download_formats.pdf; + hasPDF = true; + } else if (!currentDocument.slug.includes('api-reference-complete') && + !currentDocument.slug.includes('openapi-specification') && + !currentDocument.slug.includes('api-javascript-examples') && + !currentDocument.slug.includes('api-python-examples') && + !currentDocument.slug.includes('technical-architecture-diagram')) { + pdfPath = `/downloads/${currentDocument.slug}.pdf`; + hasPDF = true; + } let headerHTML = `
@@ -425,7 +469,7 @@ async function loadDocument(slug) { ` : ''} ${hasPDF ? ` -