Fix viewer: safe JSON embed so search cards render again.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -216,12 +216,14 @@ def viewer(
|
||||
home: Optional[str] = Query(None),
|
||||
):
|
||||
sections = fetch_sections(prefix)
|
||||
# Escape < so </script> inside text_html cannot break the page script tag
|
||||
sections_json = json.dumps(sections, ensure_ascii=False, default=str).replace("<", "\\u003c")
|
||||
home_url = "/home-image" if (home or HOME_IMAGE or (BASE_DIR.parent / "Bairaci.png").exists()) else None
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"viewer.html",
|
||||
{
|
||||
"sections_json": json.dumps(sections, ensure_ascii=False, default=str),
|
||||
"sections_json": sections_json,
|
||||
"section_count": len(sections),
|
||||
"prefix": prefix or "",
|
||||
"home_url": home_url,
|
||||
|
||||
@@ -335,21 +335,44 @@
|
||||
|
||||
<div id="toast"></div>
|
||||
|
||||
<script type="application/json" id="sections-data">{{ sections_json | safe }}</script>
|
||||
<script>
|
||||
const ALL = {{ sections_json | safe }};
|
||||
let ALL = [];
|
||||
try {
|
||||
ALL = JSON.parse(document.getElementById('sections-data').textContent || '[]');
|
||||
} catch (e) {
|
||||
console.error('sections JSON parse failed', e);
|
||||
ALL = [];
|
||||
}
|
||||
|
||||
document.getElementById('total-count').textContent = ALL.length + ' секции';
|
||||
renderEditor(ALL);
|
||||
renderKwCloud();
|
||||
doSearch();
|
||||
renderGenerator();
|
||||
function esc(s) {
|
||||
return String(s ?? '')
|
||||
.replace(/&/g,'&').replace(/</g,'<')
|
||||
.replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
function shortPath(p) {
|
||||
if (!p) return '';
|
||||
const parts = String(p).replace(/\\/g,'/').split('/');
|
||||
return parts.slice(-2).join('/');
|
||||
}
|
||||
function toast(msg, isErr) {
|
||||
const el = document.getElementById('toast');
|
||||
if (!el) return;
|
||||
el.textContent = msg;
|
||||
el.classList.toggle('error', !!isErr);
|
||||
el.classList.add('show');
|
||||
clearTimeout(toast._t);
|
||||
toast._t = setTimeout(() => el.classList.remove('show'), 2800);
|
||||
}
|
||||
|
||||
function switchTab(name) {
|
||||
const order = [{% if home_url %}'home',{% endif %}'editor','search','generator'];
|
||||
document.querySelectorAll('.tab').forEach((t,i) => t.classList.toggle('active', order[i] === name));
|
||||
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
||||
document.getElementById('tab-' + name).classList.add('active');
|
||||
const panel = document.getElementById('tab-' + name);
|
||||
if (panel) panel.classList.add('active');
|
||||
if (name === 'generator') renderGenerator();
|
||||
if (name === 'search') doSearch();
|
||||
}
|
||||
|
||||
async function startRescan() {
|
||||
@@ -703,28 +726,24 @@ ${bodyAbs}
|
||||
</body></html>`;
|
||||
}
|
||||
|
||||
// ── UTILS ─────────────────────────────
|
||||
function esc(s) {
|
||||
return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
function shortPath(p) {
|
||||
if (!p) return '';
|
||||
const parts = p.replace(/\\/g,'/').split('/');
|
||||
return parts.slice(-2).join('/');
|
||||
}
|
||||
function toast(msg, isError) {
|
||||
const el = document.getElementById('toast');
|
||||
el.textContent = msg;
|
||||
el.classList.toggle('error', !!isError);
|
||||
el.classList.add('show');
|
||||
setTimeout(() => el.classList.remove('show'), 3000);
|
||||
}
|
||||
function download(filename, content, mime) {
|
||||
const a = document.createElement('a');
|
||||
a.href = URL.createObjectURL(new Blob([content], {type: mime}));
|
||||
a.download = filename;
|
||||
a.click();
|
||||
}
|
||||
|
||||
// ── INIT ─────────────────────────────
|
||||
document.getElementById('total-count').textContent = ALL.length + ' секции';
|
||||
try {
|
||||
renderEditor(ALL);
|
||||
renderKwCloud();
|
||||
doSearch();
|
||||
renderGenerator();
|
||||
} catch (e) {
|
||||
console.error('init failed', e);
|
||||
toast('Грешка при зареждане на UI: ' + e.message, true);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user