feat(electron): added docs viewer
This commit is contained in:
38
electron/docs.json
Normal file
38
electron/docs.json
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"serif": {
|
||||||
|
"name": "SERiF",
|
||||||
|
"path": "docs/serif/html",
|
||||||
|
"desc": "Documentation for SERiF - Stellar Evolution in 4D",
|
||||||
|
"hostDocURL": "https://algebrist.ddns.net/~tboudreaux/docs/serif/html/",
|
||||||
|
"githubURL": "https://github.com/4DSTAR/SERiF",
|
||||||
|
"version": "0.0.1a",
|
||||||
|
"category": "Modeling Tools"
|
||||||
|
},
|
||||||
|
"gridfire": {
|
||||||
|
"name": "GridFire",
|
||||||
|
"path": "docs/gridfire/html",
|
||||||
|
"desc": "Documentation for GridFire - General purpose nuclear network",
|
||||||
|
"hostDocURL": "https://4d-star.github.io/GridFire/html/index.html",
|
||||||
|
"githubURL": "https://github.com/4DSTAR/GridFire",
|
||||||
|
"version": "0.6.0",
|
||||||
|
"category": "Modeling Tools"
|
||||||
|
},
|
||||||
|
"libplugin": {
|
||||||
|
"name": "libplugin",
|
||||||
|
"path": "docs/libplugin/html",
|
||||||
|
"desc": "Documentation for libplugin - Plugin framework for 4DSTAR",
|
||||||
|
"hostDocURL": "https://tboudreaux.github.io/libplugin/html/",
|
||||||
|
"githubURL": "https://github.com/4DSTAR/libplugin",
|
||||||
|
"version": "0.3.4",
|
||||||
|
"category": "Core Libraries"
|
||||||
|
},
|
||||||
|
"libcomposition": {
|
||||||
|
"name": "libcomposition",
|
||||||
|
"path": "docs/libcomposition/html",
|
||||||
|
"desc": "Documentation for libcomposition - Composition library for 4DSTAR",
|
||||||
|
"hostDocURL": "https://4d-star.github.io/libcomposition/html/",
|
||||||
|
"githubURL": "https://github.com/4DSTAR/libcomposition",
|
||||||
|
"version": "1.5.2",
|
||||||
|
"category": "Core Libraries"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,10 @@
|
|||||||
<div class="category-icon" style="background-color: #ef4444;">SL</div>
|
<div class="category-icon" style="background-color: #ef4444;">SL</div>
|
||||||
<span class="category-label">SERiF Libraries</span>
|
<span class="category-label">SERiF Libraries</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="category-item" data-category="docs" title="Documentation">
|
||||||
|
<div class="category-icon" style="background-color: #06b6d4;">📚</div>
|
||||||
|
<span class="category-label">Documentation</span>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="primary-sidebar-footer">
|
<div class="primary-sidebar-footer">
|
||||||
<div class="version-info">v1.0.0</div>
|
<div class="version-info">v1.0.0</div>
|
||||||
@@ -118,6 +122,28 @@
|
|||||||
<p>SERiF tools coming soon...</p>
|
<p>SERiF tools coming soon...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Documentation content -->
|
||||||
|
<div class="sidebar-content hidden" data-category="docs">
|
||||||
|
<div class="sidebar-header">
|
||||||
|
<h3>Documentation</h3>
|
||||||
|
</div>
|
||||||
|
<div class="docs-search-container">
|
||||||
|
<input type="text" id="docs-search" placeholder="Search documentation..." class="docs-search-input">
|
||||||
|
</div>
|
||||||
|
<div class="docs-categories">
|
||||||
|
<div class="docs-category-filter">
|
||||||
|
<select id="docs-category-filter" class="docs-category-select">
|
||||||
|
<option value="">All Categories</option>
|
||||||
|
<option value="Core Libraries">Core Libraries</option>
|
||||||
|
<option value="Modeling Tools">Modeling Tools</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="docs-list" id="docs-list">
|
||||||
|
<!-- Documentation items will be populated dynamically -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main class="content-area">
|
<main class="content-area">
|
||||||
@@ -319,6 +345,69 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Documentation Home Screen -->
|
||||||
|
<div id="docs-home" class="home-screen hidden">
|
||||||
|
<div class="category-hero">
|
||||||
|
<div class="category-hero-content">
|
||||||
|
<div class="category-icon-large" style="background-color: #06b6d4;">📚</div>
|
||||||
|
<h1 class="category-title">Documentation Viewer</h1>
|
||||||
|
<p class="category-subtitle">Browse and view Doxygen documentation for 4DSTAR libraries</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="category-content">
|
||||||
|
<div class="feature-section">
|
||||||
|
<h2>Available Documentation</h2>
|
||||||
|
<div id="docs-grid" class="docs-grid">
|
||||||
|
<!-- Documentation cards will be populated dynamically -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info-section">
|
||||||
|
<h3>About Documentation</h3>
|
||||||
|
<p>This viewer provides access to Doxygen-generated documentation for various 4DSTAR libraries and tools. Click on any library below to view its documentation in an integrated viewer.</p>
|
||||||
|
|
||||||
|
<div class="docs-features">
|
||||||
|
<h4>Features:</h4>
|
||||||
|
<ul>
|
||||||
|
<li>Browse documentation for multiple libraries</li>
|
||||||
|
<li>Integrated iframe viewer for seamless experience</li>
|
||||||
|
<li>Quick access to GitHub repositories</li>
|
||||||
|
<li>Search and filter by category</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Documentation Viewer -->
|
||||||
|
<div id="docs-viewer" class="hidden">
|
||||||
|
<header class="content-header">
|
||||||
|
<h2 id="docs-viewer-title">Documentation Viewer</h2>
|
||||||
|
<div class="action-buttons">
|
||||||
|
<button id="docs-open-external-btn" class="action-btn" title="Open in external browser">
|
||||||
|
<span>🔗</span> External
|
||||||
|
</button>
|
||||||
|
<button id="docs-github-btn" class="action-btn" title="View on GitHub">
|
||||||
|
<span>📁</span> GitHub
|
||||||
|
</button>
|
||||||
|
<button id="docs-back-btn" class="action-btn">
|
||||||
|
<span>←</span> Back
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="docs-viewer-content">
|
||||||
|
<div class="docs-info-bar">
|
||||||
|
<span id="docs-current-lib" class="docs-current-lib"></span>
|
||||||
|
<span id="docs-current-version" class="docs-current-version"></span>
|
||||||
|
</div>
|
||||||
|
<div class="docs-iframe-container">
|
||||||
|
<iframe id="docs-iframe" src="" frameborder="0" sandbox="allow-scripts allow-same-origin allow-popups allow-forms"></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="opat-view" class="hidden">
|
<div id="opat-view" class="hidden">
|
||||||
<header class="content-header">
|
<header class="content-header">
|
||||||
<h2 id="opat-title">OPAT File Inspector</h2>
|
<h2 id="opat-title">OPAT File Inspector</h2>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const eventHandlers = require('./renderer/event-handlers');
|
|||||||
const opatHandler = require('./renderer/opat-handler');
|
const opatHandler = require('./renderer/opat-handler');
|
||||||
const fillWorkflow = require('./renderer/fill-workflow');
|
const fillWorkflow = require('./renderer/fill-workflow');
|
||||||
const opatPlotting = require('./renderer/opat-plotting');
|
const opatPlotting = require('./renderer/opat-plotting');
|
||||||
|
const docsHandler = require('./renderer/docs-handler');
|
||||||
|
|
||||||
// Initialize all modules with their dependencies
|
// Initialize all modules with their dependencies
|
||||||
function initializeModules() {
|
function initializeModules() {
|
||||||
@@ -31,7 +32,8 @@ function initializeModules() {
|
|||||||
eventHandlers,
|
eventHandlers,
|
||||||
opatHandler,
|
opatHandler,
|
||||||
fillWorkflow,
|
fillWorkflow,
|
||||||
opatPlotting
|
opatPlotting,
|
||||||
|
docsHandler
|
||||||
};
|
};
|
||||||
|
|
||||||
// Initialize each module with its dependencies
|
// Initialize each module with its dependencies
|
||||||
@@ -44,6 +46,10 @@ function initializeModules() {
|
|||||||
fillWorkflow.initializeDependencies(deps);
|
fillWorkflow.initializeDependencies(deps);
|
||||||
opatPlotting.initializePlottingDependencies(deps);
|
opatPlotting.initializePlottingDependencies(deps);
|
||||||
|
|
||||||
|
// Initialize documentation handler
|
||||||
|
const docsHandlerInstance = new docsHandler(domManager, stateManager);
|
||||||
|
docsHandlerInstance.initialize();
|
||||||
|
|
||||||
console.log('[RENDERER] All modules initialized with dependencies');
|
console.log('[RENDERER] All modules initialized with dependencies');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
370
electron/renderer/docs-handler.js
Normal file
370
electron/renderer/docs-handler.js
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
/**
|
||||||
|
* Documentation Handler Module
|
||||||
|
* Manages documentation viewing functionality for the 4DSTAR Electron app
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DocsHandler {
|
||||||
|
constructor(domManager, stateManager) {
|
||||||
|
this.domManager = domManager;
|
||||||
|
this.stateManager = stateManager;
|
||||||
|
this.docsConfig = null;
|
||||||
|
this.currentDoc = null;
|
||||||
|
this.filteredDocs = [];
|
||||||
|
|
||||||
|
console.log('[DOCS_HANDLER] Documentation handler initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the documentation handler
|
||||||
|
*/
|
||||||
|
async initialize() {
|
||||||
|
try {
|
||||||
|
await this.loadDocsConfig();
|
||||||
|
this.setupEventListeners();
|
||||||
|
this.populateDocsGrid();
|
||||||
|
this.populateDocsList();
|
||||||
|
console.log('[DOCS_HANDLER] Documentation handler ready');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[DOCS_HANDLER] Failed to initialize:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load documentation configuration from docs.json
|
||||||
|
*/
|
||||||
|
async loadDocsConfig() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('./docs.json');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Failed to load docs.json: ${response.status}`);
|
||||||
|
}
|
||||||
|
this.docsConfig = await response.json();
|
||||||
|
console.log('[DOCS_HANDLER] Loaded documentation config:', Object.keys(this.docsConfig));
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[DOCS_HANDLER] Error loading docs config:', error);
|
||||||
|
// Fallback to empty config
|
||||||
|
this.docsConfig = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup event listeners for documentation interface
|
||||||
|
*/
|
||||||
|
setupEventListeners() {
|
||||||
|
// Search functionality
|
||||||
|
const searchInput = document.getElementById('docs-search');
|
||||||
|
if (searchInput) {
|
||||||
|
searchInput.addEventListener('input', (e) => {
|
||||||
|
this.filterDocs(e.target.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Category filter
|
||||||
|
const categoryFilter = document.getElementById('docs-category-filter');
|
||||||
|
if (categoryFilter) {
|
||||||
|
categoryFilter.addEventListener('change', (e) => {
|
||||||
|
this.filterDocsByCategory(e.target.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Documentation viewer controls
|
||||||
|
const backBtn = document.getElementById('docs-back-btn');
|
||||||
|
if (backBtn) {
|
||||||
|
backBtn.addEventListener('click', () => {
|
||||||
|
this.showDocsHome();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const externalBtn = document.getElementById('docs-open-external-btn');
|
||||||
|
if (externalBtn) {
|
||||||
|
externalBtn.addEventListener('click', () => {
|
||||||
|
this.openExternalDoc();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const githubBtn = document.getElementById('docs-github-btn');
|
||||||
|
if (githubBtn) {
|
||||||
|
githubBtn.addEventListener('click', () => {
|
||||||
|
this.openGitHub();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate the documentation grid on the home screen
|
||||||
|
*/
|
||||||
|
populateDocsGrid() {
|
||||||
|
const docsGrid = document.getElementById('docs-grid');
|
||||||
|
if (!docsGrid || !this.docsConfig) return;
|
||||||
|
|
||||||
|
docsGrid.innerHTML = '';
|
||||||
|
|
||||||
|
Object.entries(this.docsConfig).forEach(([key, doc]) => {
|
||||||
|
const card = this.createDocCard(key, doc);
|
||||||
|
docsGrid.appendChild(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate the documentation list in the sidebar
|
||||||
|
*/
|
||||||
|
populateDocsList() {
|
||||||
|
const docsList = document.getElementById('docs-list');
|
||||||
|
if (!docsList || !this.docsConfig) return;
|
||||||
|
|
||||||
|
this.filteredDocs = Object.entries(this.docsConfig);
|
||||||
|
this.renderDocsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a documentation card for the grid
|
||||||
|
*/
|
||||||
|
createDocCard(key, doc) {
|
||||||
|
const card = document.createElement('div');
|
||||||
|
card.className = 'docs-card';
|
||||||
|
card.dataset.docKey = key;
|
||||||
|
|
||||||
|
card.innerHTML = `
|
||||||
|
<div class="docs-card-header">
|
||||||
|
<div class="docs-card-icon">📚</div>
|
||||||
|
<h3 class="docs-card-title">${doc.name}</h3>
|
||||||
|
</div>
|
||||||
|
<p class="docs-card-desc">${doc.desc}</p>
|
||||||
|
<div class="docs-card-meta">
|
||||||
|
<span class="docs-card-version">v${doc.version}</span>
|
||||||
|
<span class="docs-card-category">${doc.category}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
card.addEventListener('click', () => {
|
||||||
|
this.openDocumentation(key, doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a documentation item for the sidebar list
|
||||||
|
*/
|
||||||
|
createDocItem(key, doc) {
|
||||||
|
const item = document.createElement('div');
|
||||||
|
item.className = 'docs-item';
|
||||||
|
item.dataset.docKey = key;
|
||||||
|
|
||||||
|
item.innerHTML = `
|
||||||
|
<div class="docs-item-name">${doc.name}</div>
|
||||||
|
<div class="docs-item-desc">${doc.desc}</div>
|
||||||
|
<div class="docs-item-meta">
|
||||||
|
<span>v${doc.version}</span>
|
||||||
|
<span class="docs-item-category">${doc.category}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
this.openDocumentation(key, doc);
|
||||||
|
});
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the filtered documentation list
|
||||||
|
*/
|
||||||
|
renderDocsList() {
|
||||||
|
const docsList = document.getElementById('docs-list');
|
||||||
|
if (!docsList) return;
|
||||||
|
|
||||||
|
docsList.innerHTML = '';
|
||||||
|
|
||||||
|
if (this.filteredDocs.length === 0) {
|
||||||
|
docsList.innerHTML = '<div class="empty-state">No documentation found</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.filteredDocs.forEach(([key, doc]) => {
|
||||||
|
const item = this.createDocItem(key, doc);
|
||||||
|
docsList.appendChild(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter documentation by search term
|
||||||
|
*/
|
||||||
|
filterDocs(searchTerm) {
|
||||||
|
const term = searchTerm.toLowerCase().trim();
|
||||||
|
|
||||||
|
if (!term) {
|
||||||
|
this.filteredDocs = Object.entries(this.docsConfig);
|
||||||
|
} else {
|
||||||
|
this.filteredDocs = Object.entries(this.docsConfig).filter(([key, doc]) => {
|
||||||
|
return doc.name.toLowerCase().includes(term) ||
|
||||||
|
doc.desc.toLowerCase().includes(term) ||
|
||||||
|
doc.category.toLowerCase().includes(term);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderDocsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter documentation by category
|
||||||
|
*/
|
||||||
|
filterDocsByCategory(category) {
|
||||||
|
if (!category) {
|
||||||
|
this.filteredDocs = Object.entries(this.docsConfig);
|
||||||
|
} else {
|
||||||
|
this.filteredDocs = Object.entries(this.docsConfig).filter(([key, doc]) => {
|
||||||
|
return doc.category === category;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderDocsList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open documentation in the viewer
|
||||||
|
*/
|
||||||
|
async openDocumentation(key, doc) {
|
||||||
|
try {
|
||||||
|
console.log('[DOCS_HANDLER] Opening documentation:', doc.name);
|
||||||
|
|
||||||
|
this.currentDoc = { key, ...doc };
|
||||||
|
|
||||||
|
// Update viewer title and info
|
||||||
|
const titleEl = document.getElementById('docs-viewer-title');
|
||||||
|
if (titleEl) {
|
||||||
|
titleEl.textContent = `${doc.name} Documentation`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const libEl = document.getElementById('docs-current-lib');
|
||||||
|
if (libEl) {
|
||||||
|
libEl.textContent = doc.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const versionEl = document.getElementById('docs-current-version');
|
||||||
|
if (versionEl) {
|
||||||
|
versionEl.textContent = `v${doc.version}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load documentation in iframe
|
||||||
|
await this.loadDocInIframe(doc);
|
||||||
|
|
||||||
|
// Show the documentation viewer
|
||||||
|
this.domManager.showView('docs-viewer');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[DOCS_HANDLER] Error opening documentation:', error);
|
||||||
|
this.showError('Failed to load documentation');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load documentation in the iframe
|
||||||
|
*/
|
||||||
|
async loadDocInIframe(doc) {
|
||||||
|
const iframe = document.getElementById('docs-iframe');
|
||||||
|
if (!iframe) return;
|
||||||
|
|
||||||
|
// Try to load local documentation first
|
||||||
|
const localPath = `./${doc.path}/index.html`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check if local documentation exists
|
||||||
|
const response = await fetch(localPath, { method: 'HEAD' });
|
||||||
|
if (response.ok) {
|
||||||
|
iframe.src = localPath;
|
||||||
|
console.log('[DOCS_HANDLER] Loaded local documentation:', localPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('[DOCS_HANDLER] Local documentation not found, trying hosted version');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to hosted documentation
|
||||||
|
if (doc.hostDocURL) {
|
||||||
|
iframe.src = doc.hostDocURL;
|
||||||
|
console.log('[DOCS_HANDLER] Loaded hosted documentation:', doc.hostDocURL);
|
||||||
|
} else {
|
||||||
|
throw new Error('No documentation source available');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the documentation home screen
|
||||||
|
*/
|
||||||
|
showDocsHome() {
|
||||||
|
this.currentDoc = null;
|
||||||
|
this.domManager.showView('docs-home');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open current documentation in external browser
|
||||||
|
*/
|
||||||
|
openExternalDoc() {
|
||||||
|
if (!this.currentDoc) return;
|
||||||
|
|
||||||
|
const url = this.currentDoc.hostDocURL;
|
||||||
|
if (url) {
|
||||||
|
// Use IPC to open in external browser
|
||||||
|
if (window.electronAPI && window.electronAPI.openExternal) {
|
||||||
|
window.electronAPI.openExternal(url);
|
||||||
|
} else {
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open current documentation's GitHub repository
|
||||||
|
*/
|
||||||
|
openGitHub() {
|
||||||
|
if (!this.currentDoc) return;
|
||||||
|
|
||||||
|
const url = this.currentDoc.githubURL;
|
||||||
|
if (url) {
|
||||||
|
// Use IPC to open in external browser
|
||||||
|
if (window.electronAPI && window.electronAPI.openExternal) {
|
||||||
|
window.electronAPI.openExternal(url);
|
||||||
|
} else {
|
||||||
|
window.open(url, '_blank');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show error message
|
||||||
|
*/
|
||||||
|
showError(message) {
|
||||||
|
console.error('[DOCS_HANDLER] Error:', message);
|
||||||
|
// You could implement a toast notification or modal here
|
||||||
|
alert(`Documentation Error: ${message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get available documentation categories
|
||||||
|
*/
|
||||||
|
getCategories() {
|
||||||
|
if (!this.docsConfig) return [];
|
||||||
|
|
||||||
|
const categories = new Set();
|
||||||
|
Object.values(this.docsConfig).forEach(doc => {
|
||||||
|
categories.add(doc.category);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Array.from(categories).sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh documentation configuration
|
||||||
|
*/
|
||||||
|
async refresh() {
|
||||||
|
await this.loadDocsConfig();
|
||||||
|
this.populateDocsGrid();
|
||||||
|
this.populateDocsList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for use in other modules
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = DocsHandler;
|
||||||
|
}
|
||||||
@@ -113,9 +113,15 @@ function showView(viewId) {
|
|||||||
opatView.classList.toggle('hidden', viewId !== 'opat-view');
|
opatView.classList.toggle('hidden', viewId !== 'opat-view');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle documentation viewer separately
|
||||||
|
const docsViewer = document.getElementById('docs-viewer');
|
||||||
|
if (docsViewer) {
|
||||||
|
docsViewer.classList.toggle('hidden', viewId !== 'docs-viewer');
|
||||||
|
}
|
||||||
|
|
||||||
// Also hide all category home screens when showing main content
|
// Also hide all category home screens when showing main content
|
||||||
const categoryHomeScreens = [
|
const categoryHomeScreens = [
|
||||||
'libplugin-home', 'opat-home', 'libconstants-home', 'serif-home'
|
'libplugin-home', 'opat-home', 'libconstants-home', 'serif-home', 'docs-home'
|
||||||
];
|
];
|
||||||
|
|
||||||
categoryHomeScreens.forEach(screenId => {
|
categoryHomeScreens.forEach(screenId => {
|
||||||
|
|||||||
@@ -209,7 +209,8 @@ function updateWelcomeScreen(category) {
|
|||||||
'libplugin': 'Welcome to libplugin',
|
'libplugin': 'Welcome to libplugin',
|
||||||
'libconstants': 'Welcome to libconstants',
|
'libconstants': 'Welcome to libconstants',
|
||||||
'opat': 'Welcome to OPAT Core',
|
'opat': 'Welcome to OPAT Core',
|
||||||
'serif': 'Welcome to SERiF Libraries'
|
'serif': 'Welcome to SERiF Libraries',
|
||||||
|
'docs': 'Welcome to Documentation'
|
||||||
};
|
};
|
||||||
|
|
||||||
const welcomeMessages = {
|
const welcomeMessages = {
|
||||||
@@ -217,7 +218,8 @@ function updateWelcomeScreen(category) {
|
|||||||
'libplugin': 'Bundle management tools for 4DSTAR plugins.',
|
'libplugin': 'Bundle management tools for 4DSTAR plugins.',
|
||||||
'libconstants': 'Constants tools coming soon...',
|
'libconstants': 'Constants tools coming soon...',
|
||||||
'opat': 'OPAT tools coming soon...',
|
'opat': 'OPAT tools coming soon...',
|
||||||
'serif': 'SERiF tools coming soon...'
|
'serif': 'SERiF tools coming soon...',
|
||||||
|
'docs': 'Browse and view Doxygen documentation for 4DSTAR libraries.'
|
||||||
};
|
};
|
||||||
|
|
||||||
const welcomeTitle = document.querySelector('.welcome-title');
|
const welcomeTitle = document.querySelector('.welcome-title');
|
||||||
@@ -231,8 +233,8 @@ function updateWelcomeScreen(category) {
|
|||||||
function showCategoryHomeScreen(category) {
|
function showCategoryHomeScreen(category) {
|
||||||
const views = [
|
const views = [
|
||||||
'welcome-screen', 'libplugin-home', 'opat-home',
|
'welcome-screen', 'libplugin-home', 'opat-home',
|
||||||
'libconstants-home', 'serif-home', 'opat-view', 'libplugin-view',
|
'libconstants-home', 'serif-home', 'docs-home', 'opat-view', 'libplugin-view',
|
||||||
'bundle-view', 'keys-view', 'create-bundle-form', 'plugin-view'
|
'bundle-view', 'keys-view', 'create-bundle-form', 'plugin-view', 'docs-viewer'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Hide all views
|
// Hide all views
|
||||||
@@ -248,6 +250,7 @@ function showCategoryHomeScreen(category) {
|
|||||||
'opat': 'opat-home',
|
'opat': 'opat-home',
|
||||||
'libconstants': 'libconstants-home',
|
'libconstants': 'libconstants-home',
|
||||||
'serif': 'serif-home',
|
'serif': 'serif-home',
|
||||||
|
'docs': 'docs-home',
|
||||||
'keys': 'keys-view'
|
'keys': 'keys-view'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3513,3 +3513,303 @@ body.dark-mode .opat-table-tag-highlight {
|
|||||||
.help-content {
|
.help-content {
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===== DOCUMENTATION VIEWER STYLES ===== */
|
||||||
|
|
||||||
|
/* Documentation sidebar styles */
|
||||||
|
.docs-search-container {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-search-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-search-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-categories {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-category-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 6px 10px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-item {
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-item:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
background-color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-item-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-item-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-light);
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-item-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-item-category {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Documentation home screen styles */
|
||||||
|
.docs-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card {
|
||||||
|
background: var(--background-color);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-desc {
|
||||||
|
color: var(--text-light);
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-version {
|
||||||
|
background-color: var(--hover-color);
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-category {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-features {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-features h4 {
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-features ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-features li {
|
||||||
|
padding: 4px 0;
|
||||||
|
color: var(--text-light);
|
||||||
|
position: relative;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-features li::before {
|
||||||
|
content: "✓";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
color: var(--primary-color);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Documentation viewer styles */
|
||||||
|
.docs-viewer-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: calc(100vh - 80px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-info-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background-color: var(--hover-color);
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-current-lib {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-current-version {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
color: white;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-iframe-container {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#docs-iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn:hover {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
background-color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn span {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive documentation styles */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.docs-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-header {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-icon {
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-card-title {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-info-bar {
|
||||||
|
padding: 8px 15px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
padding: 6px 10px;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user