alpinux-static/app/templates/preview_image.html
Alpinux c503f5e074 feat: redimensionnement d'images depuis la prévisualisation CDN
Ajoute une carte interactive sous chaque aperçu d'image permettant de
générer des copies redimensionnées directement dans le même dossier CDN.

- Route POST /resize avec Pillow (PNG, JPG, ICO) et cairosvg optionnel (SVG)
- Tailles disponibles : 32, 64, 100, 128, 200, 300, 500, 600, 1024 px (carré)
- Formats : png, jpg, ico (svg uniquement si la source est déjà SVG)
- Nommage automatique : {nom}_{taille}x{taille}.{ext}
- UI chips cliquables, soumission AJAX, retour avec liens directs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 08:26:51 +02:00

131 lines
4.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %}
{% block title %}{{ filename }}{% endblock %}
{% block content %}
<section class="card">
<div class="preview-header">
<a href="{{ url_for('browse', subpath=parent_path) if parent_path else url_for('browse') }}"
class="back-link">← Retour</a>
<h1>{{ filename }}</h1>
{% include '_preview_nav.html' %}
</div>
<div class="preview-meta">
<span>Taille : <strong>{{ filesize }}</strong></span>
<span>Modifié : <strong>{{ mtime.strftime('%d/%m/%Y %H:%M') }}</strong></span>
<a href="{{ raw_url }}" download="{{ filename }}" class="btn btn-primary" style="margin-left:auto">
Télécharger
</a>
</div>
</section>
<div class="preview-image-wrap">
<img src="{{ raw_url }}" alt="{{ filename }}">
</div>
<section class="card resize-card">
<h2>Créer des copies redimensionnées</h2>
<div class="resize-body">
<div class="resize-group">
<div class="resize-group-label">Tailles (px)</div>
<div class="resize-chips">
{% for size in [32, 64, 100, 128, 200, 300, 500, 600, 1024] %}
<label class="chip">
<input type="checkbox" class="resize-sz" value="{{ size }}">
<span>{{ size }}×{{ size }}</span>
</label>
{% endfor %}
</div>
</div>
<div class="resize-group">
<div class="resize-group-label">Formats</div>
<div class="resize-chips">
{% for fmt in ['png', 'jpg', 'ico'] %}
<label class="chip">
<input type="checkbox" class="resize-fmt" value="{{ fmt }}">
<span>.{{ fmt }}</span>
</label>
{% endfor %}
<label class="chip {% if ext != '.svg' %}chip--disabled{% endif %}"
title="{% if ext != '.svg' %}SVG uniquement disponible si la source est SVG{% endif %}">
<input type="checkbox" class="resize-fmt" value="svg"
{% if ext != '.svg' %}disabled{% endif %}>
<span>.svg</span>
</label>
</div>
</div>
<div class="resize-actions">
<button id="resize-btn" class="btn btn-primary" disabled>Générer les copies</button>
<span class="resize-hint">Les fichiers sont créés dans le même dossier</span>
</div>
<div id="resize-result" class="resize-result" style="display:none"></div>
</div>
</section>
<script>
(function () {
const szCbs = document.querySelectorAll('.resize-sz');
const fmtCbs = document.querySelectorAll('.resize-fmt');
const btn = document.getElementById('resize-btn');
const result = document.getElementById('resize-result');
const PATH = {{ subpath | tojson }};
const URL_RESIZE = {{ url_for('resize_image') | tojson }};
function canSubmit() {
return Array.from(szCbs).some(c => c.checked)
&& Array.from(fmtCbs).some(c => c.checked);
}
[...szCbs, ...fmtCbs].forEach(c => c.addEventListener('change', () => {
btn.disabled = !canSubmit();
}));
btn.addEventListener('click', async () => {
const fd = new FormData();
fd.append('path', PATH);
szCbs.forEach(c => { if (c.checked) fd.append('sizes', c.value); });
fmtCbs.forEach(c => { if (c.checked) fd.append('formats', c.value); });
btn.disabled = true;
btn.textContent = 'Génération en cours…';
result.style.display = 'none';
try {
const resp = await fetch(URL_RESIZE, { method: 'POST', body: fd });
const data = await resp.json();
let html = '';
if (data.created && data.created.length) {
html += '<p class="resize-ok-title">✓ ' + data.created.length + ' fichier(s) créé(s)</p>';
html += '<ul class="resize-list">';
data.created.forEach(f => {
html += '<li><a href="/browse/' + f.path + '">' + f.name + '</a></li>';
});
html += '</ul>';
}
if (data.errors && data.errors.length) {
html += '<p class="resize-err-title">⚠ ' + data.errors.length + ' erreur(s)</p>';
html += '<ul class="resize-list resize-list--err">';
data.errors.forEach(e => {
html += '<li><code>' + (e.name || '') + '</code> : ' + e.reason + '</li>';
});
html += '</ul>';
}
if (!html) html = '<p class="resize-none">Aucun fichier généré.</p>';
result.innerHTML = html;
} catch (_) {
result.innerHTML = '<p class="resize-err-title">Erreur réseau.</p>';
}
result.style.display = 'block';
btn.textContent = 'Générer les copies';
btn.disabled = !canSubmit();
});
})();
</script>
{% endblock %}