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>
131 lines
4.4 KiB
HTML
131 lines
4.4 KiB
HTML
{% 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 %}
|