159 lines
6.3 KiB
HTML
159 lines
6.3 KiB
HTML
|
|
{{define "title"}}{{.Page.Title}} — Talas Wiki{{end}}
|
||
|
|
{{define "pagePath"}}{{encodeURL .Page.URLPath}}{{end}}
|
||
|
|
|
||
|
|
{{define "content"}}
|
||
|
|
<div class="breadcrumb">
|
||
|
|
<a href="/">/</a>
|
||
|
|
{{range breadcrumbs .Page.URLPath}}
|
||
|
|
/ <a href="/wiki/{{encodeURL .path}}">{{.name}}</a>
|
||
|
|
{{end}}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="page-header">
|
||
|
|
<h1>{{.Page.Title}}</h1>
|
||
|
|
<div class="page-actions">
|
||
|
|
{{if not .ReadOnly}}
|
||
|
|
<a href="/edit/{{encodeURL .Page.URLPath}}" class="btn-edit">editer</a>
|
||
|
|
<a href="/propose/{{encodeURL .Page.URLPath}}" class="btn-edit">proposer</a>
|
||
|
|
<a href="/rename/{{encodeURL .Page.URLPath}}" class="btn-edit">renommer</a>
|
||
|
|
{{end}}
|
||
|
|
<a href="/history/{{encodeURL .Page.URLPath}}" class="btn-edit">historique</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="page-meta">
|
||
|
|
{{if .Page.Domain}}<a href="/wiki/{{encodeURL .Page.Domain}}" class="meta-domain-colored" style="background:{{.DomainColor}}22;color:{{.DomainColor}}">{{.Page.Domain}}</a>{{end}}
|
||
|
|
<span class="meta-date">{{formatDate .Page.ModTime}}</span>
|
||
|
|
<span class="meta-size">{{humanSize .Page.Size}}</span>
|
||
|
|
{{if .WordCount}}<span class="meta-reading">{{.WordCount}} mots · {{.ReadingMinutes}} min</span>{{end}}
|
||
|
|
{{if .IsStale}}<span class="meta-stale" title="Non modifie depuis plus de 90 jours">ancien</span>{{end}}
|
||
|
|
{{if .Viewers}}<span class="meta-viewers" title="Lecteurs actifs">{{.Viewers}} actif{{if gt .Viewers 1}}s{{end}}</span>{{end}}
|
||
|
|
{{range .Page.Tags}}<a href="/tags/{{.}}" class="meta-tag">{{.}}</a>{{end}}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div class="page-with-toc">
|
||
|
|
{{if .TOC}}
|
||
|
|
<nav class="toc" id="toc">
|
||
|
|
<div class="toc-title">Sommaire</div>
|
||
|
|
<ul>{{range .TOC}}<li class="toc-level-{{.Level}}"><a href="#{{.ID}}">{{.Text}}</a></li>{{end}}</ul>
|
||
|
|
</nav>
|
||
|
|
{{end}}
|
||
|
|
<article class="page-content" id="page-content">{{.Content}}</article>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{{if .Similar}}
|
||
|
|
<div class="similar-pages">
|
||
|
|
<h3>Voir aussi</h3>
|
||
|
|
<div class="similar-list">
|
||
|
|
{{range .Similar}}
|
||
|
|
<a href="/wiki/{{encodeURL .Page.URLPath}}" class="similar-item">{{.Page.Title}} <span class="similar-domain">{{.Page.Domain}}</span></a>
|
||
|
|
{{end}}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
{{end}}
|
||
|
|
|
||
|
|
{{if .Backlinks}}
|
||
|
|
<div class="backlinks">
|
||
|
|
<h3>Pages liees</h3>
|
||
|
|
<ul>{{range .Backlinks}}<li><a href="/wiki/{{encodeURL .URLPath}}">{{.Title}}</a> <span class="backlink-domain">{{.Domain}}</span></li>{{end}}</ul>
|
||
|
|
</div>
|
||
|
|
{{end}}
|
||
|
|
|
||
|
|
<div class="comments-section" id="comments">
|
||
|
|
<h3>Commentaires</h3>
|
||
|
|
{{range .Comments}}
|
||
|
|
<div class="comment">
|
||
|
|
<div class="comment-meta">{{.Author}} · {{formatDateShort .CreatedAt}}</div>
|
||
|
|
<div class="comment-content">{{.Content}}</div>
|
||
|
|
{{if not $.ReadOnly}}
|
||
|
|
<form method="POST" action="/comment-delete" class="comment-delete">
|
||
|
|
<input type="hidden" name="page" value="{{$.Page.URLPath}}">
|
||
|
|
<input type="hidden" name="id" value="{{.ID}}">
|
||
|
|
<button type="submit" class="comment-del-btn">supprimer</button>
|
||
|
|
</form>
|
||
|
|
{{end}}
|
||
|
|
</div>
|
||
|
|
{{end}}
|
||
|
|
{{if not .ReadOnly}}
|
||
|
|
<form method="POST" action="/comment/{{encodeURL .Page.URLPath}}" class="comment-form">
|
||
|
|
<textarea name="comment" class="comment-input" placeholder="Ajouter un commentaire..." rows="2"></textarea>
|
||
|
|
<button type="submit" class="fix-btn" style="margin-top:4px">commenter</button>
|
||
|
|
</form>
|
||
|
|
{{end}}
|
||
|
|
</div>
|
||
|
|
{{end}}
|
||
|
|
|
||
|
|
{{define "scripts"}}
|
||
|
|
<script>
|
||
|
|
// WebSocket live reload
|
||
|
|
(function(){
|
||
|
|
var pagePath = '{{.Page.URLPath}}';
|
||
|
|
try {
|
||
|
|
var proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||
|
|
var ws = new WebSocket(proto + '//' + location.host + '/ws?page=' + encodeURIComponent(pagePath));
|
||
|
|
ws.onmessage = function(e) {
|
||
|
|
var msg = JSON.parse(e.data);
|
||
|
|
if (msg.type === 'reload' && msg.page === pagePath) {
|
||
|
|
location.reload();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
} catch(e) {}
|
||
|
|
})();
|
||
|
|
|
||
|
|
// Hover preview on wikilinks
|
||
|
|
document.querySelectorAll('.wikilink').forEach(function(link) {
|
||
|
|
var timer = null;
|
||
|
|
var tooltip = null;
|
||
|
|
link.addEventListener('mouseenter', function(e) {
|
||
|
|
var href = this.getAttribute('href');
|
||
|
|
if (!href || !href.startsWith('/wiki/')) return;
|
||
|
|
var path = href.replace('/wiki/', '');
|
||
|
|
timer = setTimeout(function() {
|
||
|
|
fetch('/api/page-preview?path=' + encodeURIComponent(path))
|
||
|
|
.then(function(r) { return r.json(); })
|
||
|
|
.then(function(data) {
|
||
|
|
tooltip = document.createElement('div');
|
||
|
|
tooltip.className = 'link-tooltip';
|
||
|
|
tooltip.innerHTML = '<strong>' + data.title + '</strong><br><span style="color:var(--text-dim);font-size:10px">' + data.domain + '</span><br><span style="font-size:11px">' + (data.summary || '') + '</span>';
|
||
|
|
document.body.appendChild(tooltip);
|
||
|
|
var rect = link.getBoundingClientRect();
|
||
|
|
tooltip.style.top = (rect.bottom + window.scrollY + 4) + 'px';
|
||
|
|
tooltip.style.left = Math.min(rect.left, window.innerWidth - 320) + 'px';
|
||
|
|
});
|
||
|
|
}, 400);
|
||
|
|
});
|
||
|
|
link.addEventListener('mouseleave', function() {
|
||
|
|
clearTimeout(timer);
|
||
|
|
if (tooltip) { tooltip.remove(); tooltip = null; }
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// Minimap
|
||
|
|
(function(){
|
||
|
|
var content = document.getElementById('page-content');
|
||
|
|
if (!content || content.offsetHeight < 1500) return;
|
||
|
|
var minimap = document.createElement('div');
|
||
|
|
minimap.className = 'minimap';
|
||
|
|
var scale = 100 / content.offsetHeight;
|
||
|
|
var headings = content.querySelectorAll('h1,h2,h3');
|
||
|
|
headings.forEach(function(h) {
|
||
|
|
var dot = document.createElement('div');
|
||
|
|
dot.className = 'minimap-dot';
|
||
|
|
dot.style.top = (h.offsetTop * scale) + 'px';
|
||
|
|
dot.title = h.textContent;
|
||
|
|
dot.addEventListener('click', function() { h.scrollIntoView({behavior:'smooth'}); });
|
||
|
|
minimap.appendChild(dot);
|
||
|
|
});
|
||
|
|
// Viewport indicator
|
||
|
|
var viewport = document.createElement('div');
|
||
|
|
viewport.className = 'minimap-viewport';
|
||
|
|
minimap.appendChild(viewport);
|
||
|
|
window.addEventListener('scroll', function() {
|
||
|
|
var pct = window.scrollY / (document.body.scrollHeight - window.innerHeight);
|
||
|
|
viewport.style.top = (pct * 100) + 'px';
|
||
|
|
});
|
||
|
|
document.querySelector('.main').appendChild(minimap);
|
||
|
|
})();
|
||
|
|
</script>
|
||
|
|
{{end}}
|