<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kotchasan PHP Framework - Documentation</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/php.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/11.1.1/marked.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--bg-primary: #ffffff;
--bg-secondary: #f8f9fa;
--bg-tertiary: #e9ecef;
--text-primary: #212529;
--text-secondary: #6c757d;
--border-color: #dee2e6;
--accent: #0d6efd;
--accent-hover: #0b5ed7;
--sidebar-width: 280px;
--header-height: 64px;
}
[data-theme="dark"] {
--bg-primary: #1a1d23;
--bg-secondary: #22252b;
--bg-tertiary: #2d3139;
--text-primary: #e9ecef;
--text-secondary: #adb5bd;
--border-color: #3d4149;
--accent: #4dabf7;
--accent-hover: #339af0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: var(--bg-primary);
color: var(--text-primary);
transition: background 0.3s, color 0.3s;
}
/* Header */
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--header-height);
background: var(--bg-secondary);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
padding: 0 2rem;
z-index: 1000;
gap: 2rem;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: var(--accent);
text-decoration: none;
display: flex;
align-items: center;
gap: 0.5rem;
}
.logo-icon {
width: 32px;
height: 32px;
background: linear-gradient(135deg, var(--accent), var(--accent-hover));
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
}
.header-controls {
margin-left: auto;
display: flex;
gap: 1rem;
align-items: center;
}
.btn {
padding: 0.5rem 1rem;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
transition: all 0.3s;
background: var(--bg-tertiary);
color: var(--text-primary);
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn:hover {
background: var(--accent);
color: white;
transform: translateY(-2px);
}
/* Sidebar */
.sidebar {
position: fixed;
left: 0;
top: var(--header-height);
width: var(--sidebar-width);
height: calc(100vh - var(--header-height));
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
overflow-y: auto;
padding: 1.5rem;
}
.file-tree {
list-style: none;
}
.file-item {
padding: 0.5rem 1rem;
margin: 0.25rem 0;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
color: var(--text-primary);
display: flex;
align-items: center;
gap: 0.5rem;
}
.file-item:hover {
background: var(--bg-tertiary);
transform: translateX(4px);
}
.file-item.active {
background: var(--accent);
color: white;
}
.file-icon {
width: 16px;
height: 16px;
}
/* Main Content */
.main-content {
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
padding: 3rem;
max-width: 1200px;
}
.content-wrapper {
background: var(--bg-secondary);
border-radius: 12px;
padding: 3rem;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
/* Markdown Styles */
.markdown-content h1 {
font-size: 2.5rem;
margin-bottom: 1rem;
color: var(--text-primary);
border-bottom: 2px solid var(--border-color);
padding-bottom: 0.5rem;
}
.markdown-content h2 {
font-size: 2rem;
margin: 2rem 0 1rem;
color: var(--text-primary);
}
.markdown-content h3 {
font-size: 1.5rem;
margin: 1.5rem 0 1rem;
color: var(--text-primary);
}
.markdown-content p {
line-height: 1.8;
margin-bottom: 1rem;
color: var(--text-secondary);
}
.markdown-content code {
background: var(--bg-tertiary);
padding: 0.2rem 0.4rem;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 0.9em;
}
.markdown-content pre {
background: #282c34;
border-radius: 8px;
padding: 1.5rem;
overflow-x: auto;
margin: 1.5rem 0;
}
.markdown-content pre code {
background: none;
padding: 0;
color: #abb2bf;
}
.markdown-content ul,
.markdown-content ol {
margin: 1rem 0 1rem 2rem;
color: var(--text-secondary);
}
.markdown-content li {
margin: 0.5rem 0;
line-height: 1.8;
}
.markdown-content a {
color: var(--accent);
text-decoration: none;
}
.markdown-content a:hover {
text-decoration: underline;
}
.markdown-content blockquote {
border-left: 4px solid var(--accent);
padding-left: 1rem;
margin: 1rem 0;
color: var(--text-secondary);
font-style: italic;
}
.markdown-content table {
width: 100%;
border-collapse: collapse;
margin: 1.5rem 0;
}
.markdown-content th,
.markdown-content td {
border: 1px solid var(--border-color);
padding: 0.75rem;
text-align: left;
}
.markdown-content th {
background: var(--bg-tertiary);
font-weight: 600;
}
/* Empty State */
.empty-state {
text-align: center;
padding: 4rem 2rem;
color: var(--text-secondary);
}
.empty-state-icon {
font-size: 4rem;
margin-bottom: 1rem;
opacity: 0.5;
}
.empty-state h2 {
margin-bottom: 1rem;
}
.file-upload {
margin-top: 2rem;
padding: 2rem;
border: 2px dashed var(--border-color);
border-radius: 12px;
cursor: pointer;
transition: all 0.3s;
}
.file-upload:hover {
border-color: var(--accent);
background: var(--bg-tertiary);
}
.file-upload input {
display: none;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: var(--bg-secondary);
}
::-webkit-scrollbar-thumb {
background: var(--border-color);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--text-secondary);
}
/* Responsive */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
transition: transform 0.3s;
}
.sidebar.open {
transform: translateX(0);
}
.main-content {
margin-left: 0;
padding: 1.5rem;
}
.content-wrapper {
padding: 1.5rem;
}
.header {
padding: 0 1rem;
}
}
</style>
</head>
<body>
<header class="header">
<a href="#" class="logo">
<div class="logo-icon">K</div>
Kotchasan Framework
</a>
<div class="header-controls">
<button class="btn" id="langBtn">🌐 TH</button>
<button class="btn" id="themeBtn">🌙 Dark</button>
</div>
</header>
<aside class="sidebar" id="sidebar">
<div class="file-tree" id="fileTree">
<div class="empty-state">
<div class="empty-state-icon">📁</div>
<p>ไม่พบไฟล์เอกสาร</p>
</div>
</div>
</aside>
<main class="main-content">
<div class="content-wrapper">
<div class="markdown-content" id="content">
<div class="empty-state">
<div class="empty-state-icon">📚</div>
<h2>ยินดีต้อนรับสู่ Kotchasan PHP Framework</h2>
<p>กรุณาอัปโหลดไฟล์ .md จากโฟลเดอร์ docs/ เพื่อแสดงเอกสาร</p>
<div class="file-upload" onclick="document.getElementById('fileInput').click()">
<p>📤 คลิกเพื่ออัปโหลดไฟล์ .md</p>
<input type="file" id="fileInput" accept=".md" multiple>
</div>
</div>
</div>
</div>
</main>
<script>
// State
let currentLang = 'th';
let currentTheme = 'light';
let docs = {};
let currentDoc = null;
// Elements
const themeBtn = document.getElementById('themeBtn');
const langBtn = document.getElementById('langBtn');
const fileInput = document.getElementById('fileInput');
const fileTree = document.getElementById('fileTree');
const content = document.getElementById('content');
// Theme Toggle
themeBtn.addEventListener('click', () => {
currentTheme = currentTheme === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', currentTheme);
themeBtn.textContent = currentTheme === 'light' ? '🌙 Dark' : '☀️ Light';
});
// Language Toggle
langBtn.addEventListener('click', () => {
currentLang = currentLang === 'th' ? 'en' : 'th';
langBtn.textContent = currentLang === 'th' ? '🌐 TH' : '🌐 EN';
if (currentDoc) {
loadDocument(currentDoc);
}
});
// File Upload
fileInput.addEventListener('change', async (e) => {
const files = Array.from(e.target.files);
for (const file of files) {
if (file.name.endsWith('.md')) {
const text = await file.text();
const fileName = file.name.replace('.md', '');
// Store with language detection
const lang = fileName.includes('.th') || fileName.includes('_th') ? 'th' : 'en';
const baseName = fileName.replace(/[._](th|en)$/, '');
if (!docs[baseName]) {
docs[baseName] = {};
}
docs[baseName][lang] = text;
}
}
renderFileTree();
// Load first document
const firstDoc = Object.keys(docs)[0];
if (firstDoc) {
loadDocument(firstDoc);
}
});
// Render File Tree
function renderFileTree() {
const docNames = Object.keys(docs);
if (docNames.length === 0) {
fileTree.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">📁</div>
<p>ไม่พบไฟล์เอกสาร</p>
</div>
`;
return;
}
fileTree.innerHTML = docNames.map(name => `
<div class="file-item" onclick="loadDocument('${name}')">
<span class="file-icon">📄</span>
${name}
</div>
`).join('');
}
// Load Document
function loadDocument(docName) {
currentDoc = docName;
const docContent = docs[docName][currentLang] || docs[docName]['en'] || docs[docName]['th'];
if (!docContent) {
content.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">❌</div>
<h2>ไม่พบเอกสาร</h2>
<p>ไม่มีเอกสารภาษา ${currentLang} สำหรับ ${docName}</p>
</div>
`;
return;
}
// Convert markdown to HTML
const html = marked.parse(docContent);
content.innerHTML = html;
// Highlight code blocks
content.querySelectorAll('pre code').forEach((block) => {
hljs.highlightElement(block);
});
// Update active state
document.querySelectorAll('.file-item').forEach(item => {
item.classList.remove('active');
if (item.textContent.trim() === docName) {
item.classList.add('active');
}
});
}
// Load documentation from docs folder
async function loadDocsFromFolder() {
try {
// Load Thai documentation
const thFiles = await loadMarkdownFiles('docs/th/');
// Load English documentation
const enFiles = await loadMarkdownFiles('docs/en/');
// Merge documentation
docs = {};
// Process Thai files
Object.keys(thFiles).forEach(fileName => {
if (!docs[fileName]) {
docs[fileName] = {};
}
docs[fileName]['th'] = thFiles[fileName];
});
// Process English files
Object.keys(enFiles).forEach(fileName => {
if (!docs[fileName]) {
docs[fileName] = {};
}
docs[fileName]['en'] = enFiles[fileName];
});
renderFileTree();
// Load first available document
const firstDoc = Object.keys(docs)[0];
if (firstDoc) {
loadDocument(firstDoc);
} else {
// No documents found
showNoDocsError();
}
} catch (error) {
console.error('Error loading documentation:', error);
// Show error message
showNoDocsError();
}
}
// Load markdown files from a directory
async function loadMarkdownFiles(directory) {
const files = {};
try {
// Try to load common documentation files
const commonFiles = ['index', 'installation', 'getting-started', 'api-reference', 'usage-guide', 'developer-guide'];
for (const fileName of commonFiles) {
try {
const response = await fetch(`${directory}${fileName}.md`);
if (response.ok) {
const content = await response.text();
files[fileName] = content;
}
} catch (error) {
console.warn(`Could not load ${directory}${fileName}.md:`, error);
}
}
} catch (error) {
console.warn(`Could not load files from ${directory}:`, error);
}
return files;
}
// Show error message when no documentation is found
function showNoDocsError() {
content.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">❌</div>
<h2>ไม่พบเอกสาร</h2>
<p>ไม่พบไฟล์เอกสารในโฟลเดอร์ docs/</p>
<p>กรุณาตรวจสอบว่า:</p>
<ul style="text-align: left; max-width: 400px; margin: 1rem auto;">
<li>มีไฟล์ .md ในโฟลเดอร์ docs/th/ และ docs/en/</li>
<li>ชื่อไฟล์ถูกต้อง (ใช้ภาษาอังกฤษ, เครื่องหมาย - แทนช่องว่าง)</li>
<li>ไฟล์มีสิทธิ์การอ่าน</li>
</ul>
</div>
`;
fileTree.innerHTML = `
<div class="empty-state">
<div class="empty-state-icon">📁</div>
<p>ไม่พบไฟล์เอกสาร</p>
</div>
`;
}
// Initialize documentation
loadDocsFromFolder();
</script>
</body>
</html>