talas-group/talas-wiki/templates/page.html

159 lines
6.3 KiB
HTML
Raw Permalink Normal View History

{{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}}