fix(mobile): implement navigation toggle for document viewer
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 <noreply@anthropic.com>
This commit is contained in:
parent
79a280a403
commit
d8e5061873
2 changed files with 137 additions and 31 deletions
|
|
@ -3,8 +3,11 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
||||
<meta http-equiv="Pragma" content="no-cache">
|
||||
<meta http-equiv="Expires" content="0">
|
||||
<title>Framework Documentation | Tractatus AI Safety</title>
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon-new.svg">
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
|
@ -14,9 +17,11 @@
|
|||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<meta name="apple-mobile-web-app-title" content="Tractatus">
|
||||
<link rel="apple-touch-icon" href="/images/tractatus-icon.svg">
|
||||
<link rel="apple-touch-icon" href="/images/tractatus-icon-new.svg">
|
||||
|
||||
<link rel="stylesheet" href="/css/fonts.css">
|
||||
<link rel="stylesheet" href="/css/tailwind.css?v=0.1.0.1760254958072">
|
||||
<link rel="stylesheet" href="/css/tractatus-theme.min.css">
|
||||
<style>
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
|
|
@ -430,6 +435,38 @@
|
|||
#search-modal-content::-webkit-scrollbar-thumb:hover {
|
||||
background: #94a3b8;
|
||||
}
|
||||
|
||||
/* Mobile document viewer navigation */
|
||||
@media (max-width: 1023px) {
|
||||
/* Initially show sidebar, hide main content */
|
||||
aside {
|
||||
display: block;
|
||||
}
|
||||
|
||||
main#document-viewer-main {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* When document is active, hide sidebar and show content */
|
||||
body.document-active aside {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.document-active main#document-viewer-main {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/* Back button styling (mobile only) */
|
||||
#back-to-docs-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
#back-to-docs-btn {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50">
|
||||
|
|
@ -539,7 +576,7 @@
|
|||
GitHub
|
||||
</h3>
|
||||
<div class="space-y-2">
|
||||
<a href="https://github.com/AgenticGovernance/tractatus-framework"
|
||||
<a href="https://github.com/AgenticGovernance/tractatus"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center gap-2 p-2 rounded-lg hover:bg-gray-50 transition group">
|
||||
|
|
@ -551,7 +588,7 @@
|
|||
<div class="text-xs text-gray-500">Source code, examples & contributions</div>
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://github.com/AgenticGovernance/tractatus-framework#readme"
|
||||
<a href="https://github.com/AgenticGovernance/tractatus#readme"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex items-center gap-2 p-2 rounded-lg hover:bg-gray-50 transition group">
|
||||
|
|
@ -569,7 +606,17 @@
|
|||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="lg:col-span-3">
|
||||
<main id="document-viewer-main" class="lg:col-span-3">
|
||||
<!-- Back to Documents Button (Mobile Only) -->
|
||||
<button id="back-to-docs-btn"
|
||||
class="mb-4 items-center gap-2 px-4 py-2 bg-white border border-gray-300 rounded-lg shadow-sm hover:bg-gray-50 transition"
|
||||
aria-label="Back to documents list">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
|
||||
</svg>
|
||||
<span class="font-medium text-gray-700">Back to Documents</span>
|
||||
</button>
|
||||
|
||||
<div id="document-content" class="bg-white rounded-lg shadow-sm border border-gray-200 p-8">
|
||||
<div class="text-center py-12">
|
||||
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
|
|
@ -811,13 +858,13 @@
|
|||
<!-- Version Management & PWA -->
|
||||
<script src="/js/version-manager.js"></script>
|
||||
|
||||
<script src="/js/components/document-cards.js?v=0.1.0.1760254958072"></script>
|
||||
<script src="/js/docs-app.js?v=0.1.0.1760254958072"></script>
|
||||
<script src="/js/docs-search-enhanced.js?v=0.1.0.1760254958072"></script>
|
||||
<script src="/js/components/document-cards.js?v=0.1.0.1760825529624"></script>
|
||||
<script src="/js/docs-app.js?v=0.1.0.1760825529624"></script>
|
||||
<script src="/js/docs-search-enhanced.js?v=0.1.0.1760825529624"></script>
|
||||
|
||||
<!-- Internationalization -->
|
||||
<script src="/js/i18n-simple.js?v=0.1.0.1760643941"></script>
|
||||
<script src="/js/components/language-selector.js?v=0.1.0.1760643941"></script>
|
||||
<script src="/js/i18n-simple.js?v=1760818106"></script>
|
||||
<script src="/js/components/language-selector.js?v=1760818106"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -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) {
|
|||
<div class="font-medium text-gray-900">${doc.title}</div>
|
||||
</button>
|
||||
${hasPDF ? `
|
||||
<a href="/downloads/${doc.slug}.pdf"
|
||||
<a href="${pdfPath}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="doc-download-link"
|
||||
|
|
@ -308,12 +316,37 @@ async function loadDocuments() {
|
|||
}
|
||||
});
|
||||
|
||||
// Check for URL parameter to auto-expand category
|
||||
// Check for URL parameter to auto-load document or category
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const docParam = urlParams.get('doc');
|
||||
const categoryParam = urlParams.get('category');
|
||||
|
||||
// Auto-expand and navigate to category from URL parameter
|
||||
if (categoryParam && grouped[categoryParam] && grouped[categoryParam].length > 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 = `
|
||||
<div class="flex items-center justify-between mb-6 pb-4 border-b border-gray-200">
|
||||
|
|
@ -425,7 +469,7 @@ async function loadDocument(slug) {
|
|||
</button>
|
||||
` : ''}
|
||||
${hasPDF ? `
|
||||
<a href="/downloads/${currentDocument.slug}.pdf"
|
||||
<a href="${pdfPath}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="p-2 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded transition"
|
||||
|
|
@ -462,6 +506,9 @@ async function loadDocument(slug) {
|
|||
}
|
||||
}, 100);
|
||||
|
||||
// Mobile navigation: Add document-active class to show document view
|
||||
document.body.classList.add('document-active');
|
||||
|
||||
// Scroll to top
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
|
||||
|
|
@ -564,3 +611,15 @@ if (modal) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Mobile navigation: Back to documents button
|
||||
const backButton = document.getElementById('back-to-docs-btn');
|
||||
if (backButton) {
|
||||
backButton.addEventListener('click', function() {
|
||||
// Remove document-active class to show sidebar
|
||||
document.body.classList.remove('document-active');
|
||||
|
||||
// Scroll to top
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue