{{ key }}:
{% if value is sequence and value is not string %}
{% for item in value %}
{{ item }}{% if not loop.last %}, {% endif %}
{% endfor %}
{% else %}
{{ value }}
{% endif %}
{% endfor %}
{% endif %}
{{ content|safe }}
{% if toc_headings %}
📑 Contents
{% endif %}
{% endblock %}
{% block extra_script %}
// Store current file state
window.currentFile = {
name: "{{ filename }}",
modified: {{ file_info.modified }},
source: "{{ source|default('local') }}",
version: {% if version %}{{ version }}{% else %}null{% endif %}
};
// jQuery-based copy functions
$(document).ready(function() {
window.copyHtmlContent = async function() {
console.log('copyHtmlContent called');
const $btn = $('#copy-html-btn');
try {
const url = `/api/file/${window.currentFile.name}/html?source=${window.currentFile.source}${window.currentFile.version ? '&version=' + window.currentFile.version : ''}`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch HTML content');
const data = await response.json();
// Combine front matter table and content HTML with comment separators
const combinedHtml =
'\n' +
data.frontmatter_html + '\n\n' +
'\n' +
data.content_html;
await navigator.clipboard.writeText(combinedHtml);
const originalText = $btn.text();
$btn.text('✅ Copied!').css('background', '#28a745');
setTimeout(() => {
$btn.text(originalText).css('background', '');
}, 2000);
} catch (err) {
console.error('Failed to copy HTML:', err);
$btn.text('❌ Failed');
setTimeout(() => $btn.text('HTML'), 2000);
}
};
window.copyMarkdownContent = async function() {
console.log('copyMarkdownContent called');
const $btn = $('#copy-markdown-btn');
try {
const url = `/api/file/${window.currentFile.name}/markdown?source=${window.currentFile.source}${window.currentFile.version ? '&version=' + window.currentFile.version : ''}`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch markdown content');
const data = await response.json();
await navigator.clipboard.writeText(data.markdown);
const originalText = $btn.text();
$btn.text('✅ Copied!').css('background', '#28a745');
setTimeout(() => {
$btn.text(originalText).css('background', '');
}, 2000);
} catch (err) {
console.error('Failed to copy Markdown:', err);
$btn.text('❌ Failed');
setTimeout(() => $btn.text('Markdown'), 2000);
}
};
});
// Version change function
function changeVersion(newVersion) {
const currentUrl = new URL(window.location);
if (newVersion && newVersion !== '') {
currentUrl.searchParams.set('version', newVersion);
} else {
currentUrl.searchParams.delete('version');
}
window.location.href = currentUrl.toString();
}
// Viewer page refresh function
window.refreshViewer = async function() {
try {
const response = await fetch(`/api/file/${window.currentFile.name}/info`);
const fileInfo = await response.json();
// Check if file has changed
if (fileInfo.modified !== window.currentFile.modified) {
// Fetch new content with source parameter
const contentResponse = await fetch(`/api/file/${window.currentFile.name}/content?source=${window.currentFile.source}`);
const contentData = await contentResponse.json();
// Update the content
document.getElementById('markdown-content').innerHTML = contentData.html;
// Update the stored state
window.currentFile.modified = fileInfo.modified;
// Update the header metadata
document.querySelector('.file-meta').textContent =
`${fileInfo.size_human} | ${fileInfo.modified_human}`;
// Flash indication that content was updated
const content = document.getElementById('markdown-content');
content.style.animation = 'contentUpdate 0.5s';
setTimeout(() => {
content.style.animation = '';
}, 500);
// Re-style note references after content update
if (window.styleNoteReferences) {
window.styleNoteReferences();
}
// Re-add copy buttons after content update
if (window.addCopyButtons) {
window.addCopyButtons();
}
// Re-generate table of contents after content update
if (window.generateTableOfContents) {
window.generateTableOfContents();
}
// Re-initialize Mermaid diagrams after content update
if (typeof mermaid !== 'undefined') {
mermaid.init(undefined, document.querySelectorAll('.mermaid'));
}
// Re-add copy button functionality after content update
if (window.setupCopyButtons) {
window.setupCopyButtons();
}
}
} catch (error) {
console.error('Error checking for updates:', error);
}
};
// Set up auto-refresh for viewer
if (window.setupAutoRefresh) {
window.setupAutoRefresh(window.refreshViewer);
}
// TOC functionality with jQuery
$(document).ready(function() {
$('#toc-toggle').on('click', function() {
const $tocNav = $('#toc-nav');
const $tocContainer = $('#table-of-contents');
const isVisible = $tocNav.is(':visible');
if (isVisible) {
$tocNav.hide();
$(this).text('+');
$tocContainer.addClass('collapsed');
} else {
$tocNav.show();
$(this).text('−');
$tocContainer.removeClass('collapsed');
}
});
// YAML metadata toggle functionality
$('#yaml-toggle').on('click', function() {
const $yamlContent = $('#yaml-content');
const $yamlContainer = $('#yaml-metadata');
const isVisible = $yamlContent.is(':visible');
if (isVisible) {
$yamlContent.hide();
$(this).text('+');
$yamlContainer.addClass('collapsed');
} else {
$yamlContent.show();
$(this).text('−');
$yamlContainer.removeClass('collapsed');
}
});
// Smooth scroll for TOC links
$('.toc-link').on('click', function(e) {
e.preventDefault();
const targetId = $(this).attr('href').substring(1);
const $target = $('#' + targetId);
if ($target.length) {
$target[0].scrollIntoView({
behavior: 'smooth',
block: 'start'
});
$target.addClass('toc-highlight');
setTimeout(() => $target.removeClass('toc-highlight'), 2000);
}
});
// Legacy setupCopyButtons function for refresh compatibility
window.setupCopyButtons = function() {
console.log('setupCopyButtons called - using onclick handlers');
};
});
{% endblock %}