278 lines
No EOL
8.6 KiB
HTML
278 lines
No EOL
8.6 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ document.title }} - Vim Docs{% endblock %}
|
|
|
|
{% block header_title %}{{ document.title }}{% endblock %}
|
|
|
|
{% block header_actions %}
|
|
<a href="{{ url_for('main.edit_document', doc_id=document.id) }}" class="button primary">
|
|
<i class="mdi mdi-pencil"></i> Edit
|
|
</a>
|
|
<a href="{{ url_for('main.export_document', doc_id=document.id) }}" class="button">
|
|
<i class="mdi mdi-download"></i> Export
|
|
</a>
|
|
{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<!-- GitHub Markdown CSS -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown-dark.min.css">
|
|
|
|
<style>
|
|
.document-view {
|
|
padding: 20px;
|
|
background-color: #0d1117;
|
|
}
|
|
|
|
.document-metadata {
|
|
margin-bottom: 20px;
|
|
padding-bottom: 15px;
|
|
border-bottom: 1px solid var(--border-color);
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.metadata-item {
|
|
margin-right: 15px;
|
|
color: var(--text-muted);
|
|
font-size: 0.9em;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.metadata-item i {
|
|
margin-right: 5px;
|
|
}
|
|
|
|
.document-tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 5px;
|
|
}
|
|
|
|
.document-tag {
|
|
background: var(--accent-color);
|
|
color: var(--bg-color);
|
|
padding: 2px 8px;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
transition: transform 0.2s ease;
|
|
}
|
|
|
|
.document-tag:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.markdown-body {
|
|
box-sizing: border-box;
|
|
min-width: 200px;
|
|
max-width: 980px;
|
|
margin: 0 auto;
|
|
padding: 45px;
|
|
color: #c9d1d9;
|
|
}
|
|
|
|
/* GitHub-style admonitions/alerts */
|
|
.markdown-body .admonition {
|
|
padding: 1rem;
|
|
border-left: 4px solid;
|
|
margin: 1em 0;
|
|
border-radius: 6px;
|
|
background-color: rgba(175, 184, 193, 0.2);
|
|
}
|
|
|
|
.markdown-body .admonition-title {
|
|
font-weight: 600;
|
|
margin-top: 0;
|
|
}
|
|
|
|
.markdown-body .admonition-note {
|
|
border-color: #2b6eff;
|
|
background-color: rgba(43, 110, 255, 0.1);
|
|
}
|
|
|
|
.markdown-body .admonition-note .admonition-title {
|
|
color: #2b6eff;
|
|
}
|
|
|
|
.markdown-body .admonition-tip {
|
|
border-color: #3fb950;
|
|
background-color: rgba(63, 185, 80, 0.1);
|
|
}
|
|
|
|
.markdown-body .admonition-tip .admonition-title {
|
|
color: #3fb950;
|
|
}
|
|
|
|
.markdown-body .admonition-important {
|
|
border-color: #a371f7;
|
|
background-color: rgba(163, 113, 247, 0.1);
|
|
}
|
|
|
|
.markdown-body .admonition-important .admonition-title {
|
|
color: #a371f7;
|
|
}
|
|
|
|
.markdown-body .admonition-warning {
|
|
border-color: #d29922;
|
|
background-color: rgba(210, 153, 34, 0.1);
|
|
}
|
|
|
|
.markdown-body .admonition-warning .admonition-title {
|
|
color: #d29922;
|
|
}
|
|
|
|
.markdown-body .admonition-caution {
|
|
border-color: #f85149;
|
|
background-color: rgba(248, 81, 73, 0.1);
|
|
}
|
|
|
|
.markdown-body .admonition-caution .admonition-title {
|
|
color: #f85149;
|
|
}
|
|
|
|
/* Regular blockquotes */
|
|
.markdown-body blockquote {
|
|
padding: 0.5rem 1rem;
|
|
color: #8b949e;
|
|
border-left: 0.25em solid #30363d;
|
|
margin: 1em 0;
|
|
background-color: rgba(55, 65, 81, 0.1);
|
|
}
|
|
|
|
.markdown-body blockquote > :first-child {
|
|
margin-top: 0;
|
|
}
|
|
|
|
.markdown-body blockquote > :last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="document-view">
|
|
<div class="document-metadata">
|
|
<div class="metadata-item">
|
|
<i class="mdi mdi-calendar"></i>
|
|
Created: {{ document.created_date.strftime('%b %d, %Y') }}
|
|
</div>
|
|
<div class="metadata-item">
|
|
<i class="mdi mdi-update"></i>
|
|
Updated: {{ document.updated_date.strftime('%b %d, %Y') }}
|
|
</div>
|
|
{% if document.category %}
|
|
<div class="metadata-item">
|
|
<i class="mdi {{ document.category.icon }}"></i>
|
|
{{ document.category.name }}
|
|
</div>
|
|
{% endif %}
|
|
{% if document.tags %}
|
|
<div class="metadata-item document-tags">
|
|
<i class="mdi mdi-tag-multiple"></i>
|
|
{% for tag in document.tags %}
|
|
<span class="document-tag">{{ tag.name }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="markdown-body" id="document-content" data-content="{{ document.content|tojson|safe }}">
|
|
<!-- Content will be rendered here -->
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<!-- Marked.js for Markdown parsing -->
|
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Markdown rendering configuration
|
|
marked.setOptions({
|
|
renderer: new marked.Renderer(),
|
|
gfm: true,
|
|
tables: true,
|
|
breaks: false,
|
|
pedantic: false,
|
|
sanitize: false,
|
|
smartLists: true,
|
|
smartypants: false
|
|
});
|
|
|
|
// Custom renderer for GitHub-style alert blocks
|
|
const renderer = new marked.Renderer();
|
|
|
|
// Fix the link renderer
|
|
const originalLink = renderer.link;
|
|
renderer.link = function(href, title, text) {
|
|
const html = originalLink.call(this, href, title, text);
|
|
return html.replace(/^<a /, '<a target="_blank" rel="noopener noreferrer" ');
|
|
};
|
|
|
|
// Fix the blockquote renderer for GitHub-style alert blocks
|
|
const originalBlockquote = renderer.blockquote;
|
|
renderer.blockquote = function(quote) {
|
|
// Extract text without p tags
|
|
const text = quote.replace(/<\/?p>/g, '');
|
|
// Pattern for GitHub-style alerts: > [!NOTE], > [!WARNING], etc.
|
|
const admonitionRegex = /^\s*\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION|DANGER)\]\s*([\s\S]*)/i;
|
|
const match = text.match(admonitionRegex);
|
|
|
|
if (match) {
|
|
const type = match[1].toLowerCase();
|
|
const title = match[1].charAt(0).toUpperCase() + match[1].slice(1).toLowerCase();
|
|
const content = match[2] ? match[2].trim() : '';
|
|
|
|
return `<div class="admonition admonition-${type}">
|
|
<p class="admonition-title">${title}</p>
|
|
<p>${content}</p>
|
|
</div>`;
|
|
}
|
|
|
|
// Fall back to the original blockquote renderer
|
|
return originalBlockquote.call(this, quote);
|
|
};
|
|
|
|
// Apply custom renderer
|
|
marked.use({ renderer: renderer });
|
|
|
|
// Render markdown content
|
|
const documentContent = document.getElementById('document-content');
|
|
const markdownContent = documentContent.getAttribute('data-content');
|
|
|
|
console.log("Raw content attribute:", markdownContent);
|
|
|
|
try {
|
|
// Parse the JSON-encoded content (Flask's tojson filter wraps content in quotes)
|
|
const decodedContent = JSON.parse(markdownContent);
|
|
console.log("Content length:", decodedContent.length);
|
|
|
|
// Render the markdown directly without additional checks
|
|
documentContent.innerHTML = marked.parse(decodedContent);
|
|
} catch (e) {
|
|
console.error("Error rendering document:", e);
|
|
documentContent.innerHTML = '<div class="text-center p-8 text-red-500">Error displaying document content.</div>';
|
|
}
|
|
|
|
// Add linkification for dynamic links
|
|
document.querySelectorAll('#document-content a[href^="#doc:"]').forEach(link => {
|
|
const docId = link.getAttribute('href').replace('#doc:', '');
|
|
link.setAttribute('href', `/document/${docId}`);
|
|
// Don't open internal doc links in new tab
|
|
link.removeAttribute('target');
|
|
link.removeAttribute('rel');
|
|
});
|
|
|
|
// Add keyboard shortcuts
|
|
document.addEventListener('keydown', function(e) {
|
|
// 'e' to edit the current document
|
|
if (e.key === 'e' && !e.ctrlKey && !e.metaKey && !e.altKey &&
|
|
!(e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA')) {
|
|
e.preventDefault();
|
|
window.location.href = "{{ url_for('main.edit_document', doc_id=document.id) }}";
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |