317 lines
16 KiB
HTML
317 lines
16 KiB
HTML
{{define "base"}}
|
|
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{{template "title" .}}</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Space+Grotesk:wght@700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="/static/style.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github.min.css" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github-dark.min.css" media="(prefers-color-scheme: dark)">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.css">
|
|
<link rel="alternate" type="application/rss+xml" title="Talas Wiki RSS" href="/rss">
|
|
<link rel="icon" href="/static/favicon.svg" type="image/svg+xml">
|
|
</head>
|
|
<body data-page-path="{{template "pagePath" .}}">
|
|
<div class="scroll-progress" id="scroll-progress"></div>
|
|
<div class="cmd-palette" id="cmd-palette" style="display:none">
|
|
<input type="text" class="cmd-input" id="cmd-input" placeholder="Chercher une page, commande..." autocomplete="off">
|
|
<div class="cmd-results" id="cmd-results"></div>
|
|
</div>
|
|
<div class="cmd-overlay" id="cmd-overlay" style="display:none"></div>
|
|
<div class="layout">
|
|
<button class="sidebar-toggle" onclick="document.querySelector('.sidebar').classList.toggle('open')" aria-label="Menu">
|
|
<span></span><span></span><span></span>
|
|
</button>
|
|
<aside class="sidebar">
|
|
<div class="sidebar-header">
|
|
<a href="/" class="logo">>_ talas<span class="logo-dot">.</span>wiki</a>
|
|
</div>
|
|
<div class="sidebar-search">
|
|
<div class="search-wrapper">
|
|
<input type="text" id="search-input" placeholder="rechercher..." class="search-input" autocomplete="off" value="{{template "searchQuery" .}}">
|
|
<div id="search-suggestions" class="search-suggestions"></div>
|
|
</div>
|
|
</div>
|
|
<nav class="sidebar-nav">
|
|
{{range .Domains}}
|
|
<a href="/wiki/{{encodeURL .FullDir}}" class="nav-domain" style="border-left-color:{{.Color}}">{{.Number}} {{.Name}}</a>
|
|
{{end}}
|
|
<div class="nav-separator"></div>
|
|
<a href="/graph" class="nav-link">graph</a>
|
|
<a href="/dashboard" class="nav-link">dashboard</a>
|
|
<a href="/tags" class="nav-link">tags</a>
|
|
<a href="/timeline" class="nav-link">timeline</a>
|
|
<a href="/domaindeps" class="nav-link">dependencies</a>
|
|
<a href="/reviews" class="nav-link">reviews</a>
|
|
<a href="/activity" class="nav-link">activite</a>
|
|
{{if not .ReadOnly}}
|
|
<a href="/new" class="nav-link nav-new">+ nouveau</a>
|
|
{{end}}
|
|
</nav>
|
|
</aside>
|
|
<main class="main">
|
|
{{template "content" .}}
|
|
</main>
|
|
</div>
|
|
|
|
<div class="shortcuts-overlay" id="shortcuts-help">
|
|
<h4>Raccourcis</h4>
|
|
<div class="shortcut-row"><span>Rechercher</span><span class="shortcut-key">/</span></div>
|
|
<div class="shortcut-row"><span>Editer</span><span class="shortcut-key">e</span></div>
|
|
<div class="shortcut-row"><span>Graph</span><span class="shortcut-key">g</span></div>
|
|
<div class="shortcut-row"><span>Dashboard</span><span class="shortcut-key">d</span></div>
|
|
<div class="shortcut-row"><span>Nouveau</span><span class="shortcut-key">n</span></div>
|
|
<div class="shortcut-row"><span>Aide</span><span class="shortcut-key">?</span></div>
|
|
<div class="shortcut-row"><span>Fermer</span><span class="shortcut-key">Esc</span></div>
|
|
</div>
|
|
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js"></script>
|
|
<script>hljs.highlightAll();</script>
|
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
|
|
<script>
|
|
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
mermaid.initialize({
|
|
theme: 'base',
|
|
themeVariables: {
|
|
primaryColor: isDark ? '#161618' : '#EAE6DF',
|
|
primaryTextColor: isDark ? '#E8E3DB' : '#1A1A1E',
|
|
lineColor: isDark ? '#0098B5' : '#006B7F',
|
|
secondaryColor: isDark ? '#3A352D' : '#D1CFC9',
|
|
tertiaryColor: isDark ? '#0D0D0F' : '#F2EDE6'
|
|
}
|
|
});
|
|
</script>
|
|
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/katex.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.21/dist/contrib/auto-render.min.js"></script>
|
|
<script>document.addEventListener('DOMContentLoaded',function(){renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false}]})});</script>
|
|
<script>
|
|
// Live search suggestions
|
|
(function(){
|
|
var input = document.getElementById('search-input');
|
|
var box = document.getElementById('search-suggestions');
|
|
var timer = null;
|
|
if(!input || !box) return;
|
|
input.addEventListener('input', function(){
|
|
clearTimeout(timer);
|
|
var q = this.value.trim();
|
|
if(q.length < 2){ box.innerHTML=''; box.style.display='none'; return; }
|
|
timer = setTimeout(function(){
|
|
fetch('/api/suggest?q='+encodeURIComponent(q))
|
|
.then(function(r){return r.json()})
|
|
.then(function(items){
|
|
if(!items.length){ box.innerHTML=''; box.style.display='none'; return; }
|
|
box.innerHTML = items.map(function(item){
|
|
return '<a class="suggestion" href="/wiki/'+encodeURIComponent(item.path).replace(/%2F/g,'/')+'"><span class="sug-title">'+item.title+'</span><span class="sug-domain">'+item.domain+'</span></a>';
|
|
}).join('');
|
|
box.style.display='block';
|
|
});
|
|
}, 150);
|
|
});
|
|
input.addEventListener('keydown', function(e){
|
|
if(e.key==='Enter'){
|
|
e.preventDefault();
|
|
var first = box.querySelector('.suggestion');
|
|
if(first && box.style.display==='block'){ window.location = first.href; }
|
|
else { window.location = '/search?q='+encodeURIComponent(this.value); }
|
|
}
|
|
if(e.key==='Escape'){ box.innerHTML=''; box.style.display='none'; }
|
|
});
|
|
document.addEventListener('click', function(e){ if(!e.target.closest('.search-wrapper')){ box.innerHTML=''; box.style.display='none'; }});
|
|
})();
|
|
// Scroll progress bar
|
|
window.addEventListener('scroll', function(){
|
|
var h = document.documentElement.scrollHeight - window.innerHeight;
|
|
var pct = h > 0 ? (window.scrollY / h) * 100 : 0;
|
|
document.getElementById('scroll-progress').style.width = pct + '%';
|
|
});
|
|
|
|
// Command palette (Ctrl+K / Cmd+K)
|
|
var cmdPalette = document.getElementById('cmd-palette');
|
|
var cmdOverlay = document.getElementById('cmd-overlay');
|
|
var cmdInput = document.getElementById('cmd-input');
|
|
var cmdResults = document.getElementById('cmd-results');
|
|
var cmdTimer = null;
|
|
|
|
function openCmdPalette() {
|
|
cmdPalette.style.display = 'block';
|
|
cmdOverlay.style.display = 'block';
|
|
cmdInput.value = '';
|
|
cmdResults.innerHTML = '';
|
|
cmdInput.focus();
|
|
}
|
|
function closeCmdPalette() {
|
|
cmdPalette.style.display = 'none';
|
|
cmdOverlay.style.display = 'none';
|
|
}
|
|
cmdOverlay.addEventListener('click', closeCmdPalette);
|
|
cmdInput.addEventListener('input', function() {
|
|
clearTimeout(cmdTimer);
|
|
var q = this.value.trim();
|
|
if (q.length < 1) { cmdResults.innerHTML = ''; return; }
|
|
// Commands
|
|
var cmds = [
|
|
{title: 'Nouvelle page', action: '/new', match: 'new nouveau creer'},
|
|
{title: 'Graph', action: '/graph', match: 'graph graphe'},
|
|
{title: 'Dashboard', action: '/dashboard', match: 'dashboard tableau'},
|
|
{title: 'Timeline', action: '/timeline', match: 'timeline temps'},
|
|
{title: 'Tags', action: '/tags', match: 'tags'},
|
|
{title: 'Activite', action: '/activity', match: 'activite recent'},
|
|
{title: 'Export', action: '/export', match: 'export telecharger'},
|
|
];
|
|
var ql = q.toLowerCase();
|
|
var cmdMatches = cmds.filter(function(c) { return c.match.indexOf(ql) >= 0 || c.title.toLowerCase().indexOf(ql) >= 0; });
|
|
|
|
cmdTimer = setTimeout(function() {
|
|
fetch('/api/suggest?q=' + encodeURIComponent(q))
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(pages) {
|
|
var html = '';
|
|
cmdMatches.forEach(function(c) {
|
|
html += '<a class="cmd-result" href="' + c.action + '"><span class="cmd-result-title">' + c.title + '</span><span class="cmd-result-type">commande</span></a>';
|
|
});
|
|
pages.forEach(function(p) {
|
|
html += '<a class="cmd-result" href="/wiki/' + encodeURIComponent(p.path).replace(/%2F/g,'/') + '"><span class="cmd-result-title">' + p.title + '</span><span class="cmd-result-type">' + p.domain + '</span></a>';
|
|
});
|
|
cmdResults.innerHTML = html || '<div class="cmd-empty">Aucun resultat</div>';
|
|
});
|
|
}, 100);
|
|
});
|
|
cmdInput.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') { closeCmdPalette(); }
|
|
if (e.key === 'Enter') {
|
|
var first = cmdResults.querySelector('.cmd-result');
|
|
if (first) { window.location = first.href; }
|
|
}
|
|
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
|
|
e.preventDefault();
|
|
var items = cmdResults.querySelectorAll('.cmd-result');
|
|
var active = cmdResults.querySelector('.cmd-result.active');
|
|
var idx = -1;
|
|
items.forEach(function(el, i) { if (el === active) idx = i; });
|
|
if (active) active.classList.remove('active');
|
|
if (e.key === 'ArrowDown') idx = Math.min(idx + 1, items.length - 1);
|
|
else idx = Math.max(idx - 1, 0);
|
|
if (items[idx]) items[idx].classList.add('active');
|
|
}
|
|
});
|
|
|
|
// Global keyboard shortcuts
|
|
document.addEventListener('keydown', function(e){
|
|
// Cmd+K or Ctrl+K opens command palette anywhere
|
|
if ((e.metaKey || e.ctrlKey) && e.key === 'k') { e.preventDefault(); openCmdPalette(); return; }
|
|
if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA'||e.target.isContentEditable) return;
|
|
var help = document.getElementById('shortcuts-help');
|
|
if(e.key==='/'){e.preventDefault();openCmdPalette();return;}
|
|
if(e.key==='e'){var p=document.body.dataset.pagePath;if(p)window.location='/edit/'+p;return;}
|
|
if(e.key==='g'){window.location='/graph';return;}
|
|
if(e.key==='d'){window.location='/dashboard';return;}
|
|
if(e.key==='n'){window.location='/new';return;}
|
|
if(e.key==='?'){help.classList.toggle('visible');return;}
|
|
if(e.key==='Escape'){
|
|
closeCmdPalette();
|
|
help.classList.remove('visible');
|
|
document.querySelector('.sidebar').classList.remove('open');
|
|
var box=document.getElementById('search-suggestions');if(box){box.innerHTML='';box.style.display='none';}
|
|
}
|
|
});
|
|
|
|
// Content enhancements (code copy, table sort, anchor copy, lightbox)
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Code block copy buttons
|
|
document.querySelectorAll('pre code').forEach(function(block) {
|
|
var btn = document.createElement('button');
|
|
btn.className = 'code-copy-btn';
|
|
btn.textContent = 'copier';
|
|
btn.addEventListener('click', function() {
|
|
navigator.clipboard.writeText(block.textContent).then(function() {
|
|
btn.textContent = 'copie!';
|
|
setTimeout(function() { btn.textContent = 'copier'; }, 1500);
|
|
});
|
|
});
|
|
block.parentElement.style.position = 'relative';
|
|
block.parentElement.appendChild(btn);
|
|
});
|
|
|
|
// Heading anchor copy
|
|
document.querySelectorAll('.page-content h1[id], .page-content h2[id], .page-content h3[id]').forEach(function(h) {
|
|
h.style.cursor = 'pointer';
|
|
h.title = 'Copier le lien';
|
|
h.addEventListener('click', function() {
|
|
var url = window.location.origin + window.location.pathname + '#' + h.id;
|
|
navigator.clipboard.writeText(url);
|
|
h.style.color = 'var(--cyan)';
|
|
setTimeout(function() { h.style.color = ''; }, 1000);
|
|
});
|
|
});
|
|
|
|
// Sortable tables
|
|
document.querySelectorAll('.page-content table').forEach(function(table) {
|
|
var headers = table.querySelectorAll('th');
|
|
headers.forEach(function(th, colIdx) {
|
|
th.style.cursor = 'pointer';
|
|
th.title = 'Cliquer pour trier';
|
|
var asc = true;
|
|
th.addEventListener('click', function() {
|
|
var tbody = table.querySelector('tbody') || table;
|
|
var rows = Array.from(tbody.querySelectorAll('tr')).filter(function(r) { return r.querySelector('td'); });
|
|
rows.sort(function(a, b) {
|
|
var aText = (a.cells[colIdx] || {}).textContent || '';
|
|
var bText = (b.cells[colIdx] || {}).textContent || '';
|
|
return asc ? aText.localeCompare(bText, 'fr') : bText.localeCompare(aText, 'fr');
|
|
});
|
|
rows.forEach(function(r) { tbody.appendChild(r); });
|
|
asc = !asc;
|
|
headers.forEach(function(h) { h.classList.remove('sorted-asc', 'sorted-desc'); });
|
|
th.classList.add(asc ? 'sorted-desc' : 'sorted-asc');
|
|
});
|
|
});
|
|
});
|
|
|
|
// Image lightbox
|
|
document.querySelectorAll('.page-content img').forEach(function(img) {
|
|
img.style.cursor = 'zoom-in';
|
|
img.addEventListener('click', function() {
|
|
var overlay = document.createElement('div');
|
|
overlay.className = 'lightbox-overlay';
|
|
var clone = img.cloneNode();
|
|
clone.className = 'lightbox-img';
|
|
overlay.appendChild(clone);
|
|
overlay.addEventListener('click', function() { overlay.remove(); });
|
|
document.body.appendChild(overlay);
|
|
});
|
|
});
|
|
|
|
// TOC active section highlighting
|
|
var tocLinks = document.querySelectorAll('.toc a');
|
|
if (tocLinks.length > 0) {
|
|
var headingEls = [];
|
|
tocLinks.forEach(function(link) {
|
|
var id = link.getAttribute('href').substring(1);
|
|
var el = document.getElementById(id);
|
|
if (el) headingEls.push({el: el, link: link});
|
|
});
|
|
window.addEventListener('scroll', function() {
|
|
var scrollPos = window.scrollY + 100;
|
|
var active = null;
|
|
headingEls.forEach(function(item) {
|
|
if (item.el.offsetTop <= scrollPos) active = item;
|
|
});
|
|
tocLinks.forEach(function(l) { l.classList.remove('toc-active'); });
|
|
if (active) active.link.classList.add('toc-active');
|
|
});
|
|
}
|
|
});
|
|
</script>
|
|
{{template "scripts" .}}
|
|
</body>
|
|
</html>
|
|
{{end}}
|
|
|
|
{{define "title"}}Talas Wiki{{end}}
|
|
{{define "searchQuery"}}{{end}}
|
|
{{define "pagePath"}}{{end}}
|
|
{{define "scripts"}}{{end}}
|