mirror of
https://github.com/sotam0316/drawNET.git
synced 2026-04-25 12:08:36 +09:00
153 lines
7.2 KiB
JavaScript
153 lines
7.2 KiB
JavaScript
import { t } from '../i18n.js';
|
|
|
|
/**
|
|
* help_modal.js - Advanced Tabbed Markdown Guide.
|
|
* Uses marked.js for high-fidelity rendering.
|
|
*/
|
|
|
|
export async function showHelpModal() {
|
|
const modalContainer = document.getElementById('modal-container');
|
|
if (!modalContainer) return;
|
|
|
|
const existing = document.getElementById('help-modal');
|
|
if (existing) existing.remove();
|
|
|
|
const overlay = document.createElement('div');
|
|
overlay.id = 'help-modal';
|
|
overlay.className = 'modal-overlay animate-fade-in active';
|
|
|
|
overlay.innerHTML = `
|
|
<div class="modal-content glass-panel animate-scale-up" style="max-width: 900px; width: 95%; padding: 0; overflow: hidden; display: flex; flex-direction: column; height: 85vh; max-height: 1000px;">
|
|
<!-- Header -->
|
|
<div class="modal-header" style="padding: 20px 24px; border-bottom: 1px solid var(--panel-border); background: var(--item-bg); display: flex; align-items: center; justify-content: space-between;">
|
|
<div style="display: flex; align-items: center; gap: 16px;">
|
|
<div style="width: 42px; height: 42px; background: linear-gradient(135deg, #3b82f6, #6366f1); border-radius: 10px; display: flex; align-items: center; justify-content: center; color: white; box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);">
|
|
<span style="font-weight: 900; font-size: 18px;">dN</span>
|
|
</div>
|
|
<div>
|
|
<h2 style="font-size: 18px; font-weight: 900; color: var(--text-color); margin: 0;">${t('help_center') || 'drawNET Help Center'}</h2>
|
|
<p style="font-size: 11px; color: var(--sub-text); margin: 2px 0 0 0;">Engineering Systems Guide & Documentation</p>
|
|
</div>
|
|
</div>
|
|
<button class="modal-close" id="close-help" style="background: none; border: none; font-size: 24px; color: var(--sub-text); cursor: pointer; padding: 4px;">×</button>
|
|
</div>
|
|
|
|
<!-- Tab Navigation Bar -->
|
|
<div id="help-tabs" style="display: flex; background: var(--item-bg); border-bottom: 1px solid var(--panel-border); padding: 0 12px; overflow-x: auto; gap: 4px; scrollbar-width: none;">
|
|
<div style="padding: 15px 20px; color: var(--sub-text); font-size: 12px;">Initializing tabs...</div>
|
|
</div>
|
|
|
|
<!-- Content Area -->
|
|
<div id="help-modal-body" class="modal-body" style="padding: 32px 48px; overflow-y: auto; flex: 1; scrollbar-width: thin; background: rgba(var(--bg-rgb), 0.2);">
|
|
<div class="loading-state" style="text-align: center; padding: 60px; color: var(--sub-text);">
|
|
<i class="fas fa-circle-notch fa-spin"></i> Initializing Help System...
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<div class="modal-footer" style="padding: 16px 24px; background: var(--item-bg); text-align: right; border-top: 1px solid var(--panel-border);">
|
|
<button class="btn-primary" id="help-confirm" style="padding: 8px 32px; border-radius: 8px; background: var(--accent-color); color: white; border: none; cursor: pointer; font-weight: 700; transition: all 0.2s;">
|
|
${t('close_btn') || 'Close Guide'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
modalContainer.appendChild(overlay);
|
|
|
|
const tabsBar = overlay.querySelector('#help-tabs');
|
|
const contentArea = overlay.querySelector('#help-modal-body');
|
|
|
|
async function loadTabContent(fileId, tabElement) {
|
|
// Update tab styles
|
|
tabsBar.querySelectorAll('.help-tab').forEach(t => {
|
|
t.style.borderBottom = '2px solid transparent';
|
|
t.style.color = 'var(--sub-text)';
|
|
t.style.background = 'transparent';
|
|
});
|
|
tabElement.style.borderBottom = '2px solid var(--accent-color)';
|
|
tabElement.style.color = 'var(--accent-color)';
|
|
tabElement.style.background = 'rgba(59, 130, 246, 0.05)';
|
|
|
|
contentArea.innerHTML = `
|
|
<div style="text-align: center; padding: 60px; color: var(--sub-text);">
|
|
<i class="fas fa-circle-notch fa-spin"></i> Loading ${fileId}...
|
|
</div>
|
|
`;
|
|
|
|
try {
|
|
const response = await fetch(`/manual/${fileId}`);
|
|
if (!response.ok) throw new Error('File not found');
|
|
let markdown = await response.text();
|
|
|
|
// Image Path Pre-processing: Fix relative paths for marked.js
|
|
markdown = markdown.replace(/!\[(.*?)\]\((.*?)\)/g, (match, alt, src) => {
|
|
const finalSrc = (src.startsWith('http') || src.startsWith('/')) ? src : `/manual/${src}`;
|
|
return ``;
|
|
});
|
|
|
|
// Use marked library
|
|
const htmlContent = window.marked.parse(markdown);
|
|
contentArea.innerHTML = `<div class="markdown-body animate-fade-in">${htmlContent}</div>`;
|
|
contentArea.scrollTop = 0;
|
|
} catch (err) {
|
|
contentArea.innerHTML = `<div style="color: #ef4444; padding: 20px; text-align: center;">Error: ${err.message}</div>`;
|
|
}
|
|
}
|
|
|
|
// Load Help File List
|
|
try {
|
|
const response = await fetch('/api/help/list');
|
|
const helpFiles = await response.json();
|
|
|
|
tabsBar.innerHTML = ''; // Clear "Initializing"
|
|
|
|
helpFiles.forEach((file, index) => {
|
|
const tab = document.createElement('div');
|
|
tab.className = 'help-tab';
|
|
tab.dataset.id = file.id;
|
|
tab.innerText = file.title;
|
|
tab.style.cssText = `
|
|
padding: 14px 20px;
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
font-weight: 700;
|
|
color: var(--sub-text);
|
|
border-bottom: 2px solid transparent;
|
|
white-space: nowrap;
|
|
transition: all 0.2s;
|
|
`;
|
|
|
|
tab.addEventListener('mouseenter', () => {
|
|
if (tab.style.borderBottomColor === 'transparent') {
|
|
tab.style.color = 'var(--text-color)';
|
|
}
|
|
});
|
|
tab.addEventListener('mouseleave', () => {
|
|
if (tab.style.borderBottomColor === 'transparent') {
|
|
tab.style.color = 'var(--sub-text)';
|
|
}
|
|
});
|
|
|
|
tab.onclick = () => loadTabContent(file.id, tab);
|
|
tabsBar.appendChild(tab);
|
|
|
|
// Auto-load first tab
|
|
if (index === 0) loadTabContent(file.id, tab);
|
|
});
|
|
|
|
} catch (err) {
|
|
tabsBar.innerHTML = `<div style="padding: 15px; color: #ef4444;">Failed to load help list.</div>`;
|
|
}
|
|
|
|
const closeModal = () => {
|
|
overlay.classList.replace('animate-fade-in', 'animate-fade-out');
|
|
overlay.querySelector('.modal-content').classList.replace('animate-scale-up', 'animate-scale-down');
|
|
setTimeout(() => overlay.remove(), 200);
|
|
};
|
|
|
|
overlay.querySelector('#close-help').onclick = closeModal;
|
|
overlay.querySelector('#help-confirm').onclick = closeModal;
|
|
overlay.onclick = (e) => { if (e.target === overlay) closeModal(); };
|
|
}
|