addet some features
This commit is contained in:
parent
0861ae20c3
commit
97e3262506
11 changed files with 1369 additions and 208 deletions
|
@ -1,11 +1,24 @@
|
|||
from flask import Blueprint, render_template, request, current_app, redirect, url_for, abort, flash
|
||||
import os
|
||||
from app.utils.file_handler import save_file, read_markdown_file, get_file_by_uri, load_uri_map
|
||||
|
||||
from app.utils.file_handler import save_file, read_markdown_file, get_file_by_uri, load_uri_map, save_uri_map
|
||||
from app.utils.utils import convert_markdown_to_html
|
||||
main_bp = Blueprint('main', __name__)
|
||||
|
||||
@main_bp.route('/', methods=['GET'])
|
||||
@main_bp.route('/', methods=['GET', 'POST'])
|
||||
def index():
|
||||
if request.method == 'POST':
|
||||
delete_uri = request.form.get('delete_uri')
|
||||
|
||||
if delete_uri:
|
||||
uri_map = load_uri_map()
|
||||
if delete_uri in uri_map:
|
||||
# Remove the document from the URI map
|
||||
del uri_map[delete_uri]
|
||||
save_uri_map(uri_map)
|
||||
else:
|
||||
flash(f'Document with URI "{delete_uri}" not found.')
|
||||
return redirect(url_for('main.index'))
|
||||
|
||||
# List all available documents
|
||||
uri_map = load_uri_map()
|
||||
documents = [{'uri': uri, 'filename': info['filename']} for uri, info in uri_map.items()]
|
||||
|
@ -14,22 +27,52 @@ def index():
|
|||
@main_bp.route('/upload', methods=['GET', 'POST'])
|
||||
def upload_file():
|
||||
if request.method == 'POST':
|
||||
if 'file' not in request.files:
|
||||
flash('No file part')
|
||||
return redirect(request.url)
|
||||
|
||||
file = request.files['file']
|
||||
custom_uri = request.form.get('custom_uri', '').strip()
|
||||
raw_url = request.form.get('raw_url', '').strip()
|
||||
|
||||
if file.filename == '':
|
||||
flash('No file selected')
|
||||
return redirect(request.url)
|
||||
if raw_url:
|
||||
# Handle raw URL input
|
||||
try:
|
||||
import requests
|
||||
response = requests.get(raw_url)
|
||||
if response.status_code == 200:
|
||||
content = response.text
|
||||
from io import BytesIO
|
||||
file = BytesIO(content.encode('utf-8'))
|
||||
filename = raw_url.split('/')[-1]
|
||||
if not filename.endswith('.md'):
|
||||
filename += '.md'
|
||||
# Create a FileStorage-like object that save_file can handle
|
||||
from werkzeug.datastructures import FileStorage
|
||||
file = FileStorage(
|
||||
stream=file,
|
||||
filename=filename,
|
||||
content_type='text/markdown'
|
||||
)
|
||||
uri, filename = save_file(file, custom_uri if custom_uri else None)
|
||||
return redirect(url_for('main.view_file', uri=uri))
|
||||
else:
|
||||
flash('Could not fetch content from URL')
|
||||
return redirect(request.url)
|
||||
except Exception as e:
|
||||
flash(f'Error fetching URL: {str(e)}')
|
||||
return redirect(request.url)
|
||||
|
||||
elif 'file' in request.files:
|
||||
file = request.files['file']
|
||||
|
||||
if file and file.filename.endswith('.md'):
|
||||
custom_uri = request.form.get('custom_uri', '').strip()
|
||||
uri, filename = save_file(file, custom_uri if custom_uri else None)
|
||||
return redirect(url_for('main.view_file', uri=uri))
|
||||
if file.filename == '':
|
||||
flash('No file selected and no URL provided')
|
||||
return redirect(request.url)
|
||||
|
||||
if file and file.filename.endswith('.md'):
|
||||
uri, filename = save_file(file, custom_uri if custom_uri else None)
|
||||
return redirect(url_for('main.view_file', uri=uri))
|
||||
else:
|
||||
flash('Only markdown files are allowed')
|
||||
else:
|
||||
flash('Only markdown files are allowed')
|
||||
flash('Please either upload a file or provide a URL')
|
||||
return redirect(request.url)
|
||||
|
||||
return render_template('upload.html')
|
||||
|
||||
|
|
97
app/templates/base.html
Normal file
97
app/templates/base.html
Normal file
|
@ -0,0 +1,97 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
{% block head %}
|
||||
<title>Histogram Viewer</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/digitallytailored/classless@latest/classless.min.css">
|
||||
<link rel="stylesheet" href="https://esm.sh/@wooorm/starry-night@3/style/both">
|
||||
<style>
|
||||
.card {
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.1);
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.card-title {
|
||||
text-decoration: none;
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.card-title:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
position: relative;
|
||||
float: right;
|
||||
margin: 0;
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
background-color: var(--red);
|
||||
color: var(--text-color);
|
||||
border: none;
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
max-width: 65%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
// Dark mode toggle button
|
||||
const darkModeToggle = document.getElementById('darkModeToggle');
|
||||
|
||||
function setColorScheme(scheme) {
|
||||
document.documentElement.setAttribute('color-scheme', scheme);
|
||||
localStorage.setItem('color-scheme', scheme);
|
||||
}
|
||||
|
||||
function getColorScheme() {
|
||||
let scheme = localStorage.getItem('color-scheme');
|
||||
if (scheme) {
|
||||
return scheme;
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
setColorScheme(getColorScheme());
|
||||
|
||||
darkModeToggle.addEventListener('click', function () {
|
||||
const newScheme = getColorScheme() === 'dark' ? 'light' : 'dark';
|
||||
setColorScheme(newScheme);
|
||||
});
|
||||
|
||||
darkModeToggle.checked = getColorScheme() === 'dark';
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<nav>
|
||||
<a class="glass" href="{{ url_for('main.index') }}">Home</a>
|
||||
<a class="glass" href="{{ url_for('main.upload_file') }}">Upload</a>
|
||||
<button class="outline" id="darkModeToggle">Toggle Theme</button>
|
||||
</nav>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,91 +1,57 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Markdown Viewer</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/digitallytailored/classless@latest/classless.min.css">
|
||||
<style>
|
||||
.file-list {
|
||||
margin: 2em 0;
|
||||
}
|
||||
.file-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.75em;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.file-item:hover {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
.file-link {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
.file-uri {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.copy-button {
|
||||
background: #f0f0f0;
|
||||
border: 1px solid #ddd;
|
||||
padding: 0.25em 0.5em;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 3em;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
margin: 2em 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Markdown Viewer</h1>
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ url_for('main.upload_file') }}" class="button">Upload New Document</a>
|
||||
</div>
|
||||
|
||||
{% if documents %}
|
||||
<div class="file-list">
|
||||
<h2>Available Documents</h2>
|
||||
|
||||
{% for doc in documents %}
|
||||
<div class="file-item">
|
||||
<div>
|
||||
<a href="{{ url_for('main.view_file', uri=doc.uri) }}" class="file-link">
|
||||
{{ doc.filename }}
|
||||
</a>
|
||||
<span class="file-uri">URI: {{ doc.uri }}</span>
|
||||
</div>
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Markdown Viewer</h1>
|
||||
|
||||
<div>
|
||||
<a href="{{ url_for('main.upload_file') }}" class="button">Upload New Document</a>
|
||||
</div>
|
||||
|
||||
{% if documents %}
|
||||
<div class="container">
|
||||
<h2>Available Documents</h2>
|
||||
|
||||
{% for doc in documents %}
|
||||
<div class="card">
|
||||
<!-- <div>
|
||||
<a href="{{ url_for('main.view_file', uri=doc.uri) }}" class="card-title">
|
||||
{{ doc.filename }}
|
||||
</a>
|
||||
<span class="file-uri">URI: {{ doc.uri }}</span>
|
||||
</div> -->
|
||||
<div>
|
||||
<a href="{{ url_for('main.view_file', uri=doc.uri) }}" class="card-title">
|
||||
<h3>{{ doc.filename }}</h3>
|
||||
</a>
|
||||
<form action="{{ url_for('main.index') }}" method="post">
|
||||
<input type="hidden" name="delete_uri" value="{{ doc.uri }}">
|
||||
<button type="submit" class="delete-button">Delete</button>
|
||||
</form>
|
||||
<div>
|
||||
<a href="{{ url_for('main.view_file', uri=doc.uri) }}" class="button">View</a>
|
||||
<button class="copy-button" onclick="copyToClipboard('{{ url_for('main.view_file', uri=doc.uri, _external=True) }}')">
|
||||
Copy Link
|
||||
</button>
|
||||
<button class="copy-button"
|
||||
onclick="copyToClipboard('{{ url_for('main.view_file', uri=doc.uri, _external=True) }}')">Copy
|
||||
Link</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<h3>No documents available</h3>
|
||||
<p>Upload a markdown file to get started!</p>
|
||||
<a href="{{ url_for('main.upload_file') }}" class="button">Upload Now</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(function() {
|
||||
alert('Link copied to clipboard!');
|
||||
}, function() {
|
||||
alert('Failed to copy link');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div>
|
||||
<h3>No documents available</h3>
|
||||
<p>Upload a markdown file to get started!</p>
|
||||
<a href="{{ url_for('main.upload_file') }}" class="button">Upload Now</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
function copyToClipboard(text) {
|
||||
navigator.clipboard.writeText(text).then(function () {
|
||||
alert('Link copied to clipboard!');
|
||||
}, function () {
|
||||
alert('Failed to copy link');
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -1,39 +1,40 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Upload Markdown File</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/digitallytailored/classless@latest/classless.min.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Upload Markdown File</h1>
|
||||
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="messages">
|
||||
{% for message in messages %}
|
||||
<p class="alert">{{ message }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div>
|
||||
<label for="file">Select a markdown file:</label>
|
||||
<input type="file" name="file" id="file" accept=".md">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="custom_uri">Custom URI (optional):</label>
|
||||
<input type="text" name="custom_uri" id="custom_uri" placeholder="e.g., my-document">
|
||||
<small>Leave blank for an auto-generated URI</small>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<input type="submit" value="Upload">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<a href="{{ url_for('main.index') }}">Back to Home</a>
|
||||
</body>
|
||||
</html>
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Upload Markdown File</h1>
|
||||
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% if messages %}
|
||||
<div class="messages">
|
||||
{% for message in messages %}
|
||||
<p class="alert">{{ message }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<div>
|
||||
<label for="file">Select a markdown file:</label>
|
||||
<input type="file" name="file" id="file" accept=".md">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="raw_url">Or enter a URL:</label>
|
||||
<input type="url" name="raw_url" id="raw_url" placeholder="https://example.com/markdown-file.md">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="custom_uri">Custom URI (optional):</label>
|
||||
<input type="text" name="custom_uri" id="custom_uri" placeholder="e.g., my-document">
|
||||
<small>Leave blank for an auto-generated URI</small>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="glass" type="submit">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button class="glass" href="{{ url_for('main.index') }}">Back to Home</button>
|
||||
|
||||
{% endblock %}
|
|
@ -1,69 +1,104 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ filename }} - Markdown Viewer</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/digitallytailored/classless@latest/classless.min.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!--<link rel="stylesheet" href="github-markdown.css">-->
|
||||
<style>
|
||||
pre {
|
||||
background-color: #f5f5f5;
|
||||
padding: 1em;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
{% extends "base.html" %}
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- This supports light and dark mode automatically. -->
|
||||
<link rel="stylesheet" href="https://esm.sh/@wooorm/starry-night@3/style/both">
|
||||
<link rel="stylesheet" href="github-markdown.css">
|
||||
<!-- <style>
|
||||
.markdown-body {
|
||||
box-sizing: border-box;
|
||||
min-width: 200px;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding: 45px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.markdown-body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 0.5em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.document-info {
|
||||
margin-bottom: 2em;
|
||||
padding: 1em;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.markdown-body {
|
||||
box-sizing: border-box;
|
||||
min-width: 200px;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
padding: 45px;
|
||||
}
|
||||
|
||||
/* GitHub-style admonitions */
|
||||
.markdown-alert {
|
||||
padding: 0.5rem 1rem;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.markdown-alert-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.markdown-alert-title svg {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.markdown-alert-note {
|
||||
border-left: 4px solid #0969da;
|
||||
background-color: #ddf4ff;
|
||||
}
|
||||
|
||||
.markdown-alert-tip {
|
||||
border-left: 4px solid #1a7f37;
|
||||
background-color: #dafbe1;
|
||||
}
|
||||
|
||||
.markdown-alert-important {
|
||||
border-left: 4px solid #9a6700;
|
||||
background-color: #fff8c5;
|
||||
}
|
||||
|
||||
.markdown-alert-warning {
|
||||
border-left: 4px solid #9a6700;
|
||||
background-color: #fff8c5;
|
||||
}
|
||||
|
||||
.markdown-alert-caution {
|
||||
border-left: 4px solid #cf222e;
|
||||
background-color: #ffebe9;
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.markdown-alert-note {
|
||||
background-color: rgba(56, 139, 253, 0.15);
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.markdown-body {
|
||||
padding: 15px;
|
||||
}
|
||||
} </style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document-info">
|
||||
<h1>{{ filename }}</h1>
|
||||
<p>
|
||||
<strong>Permanent Link:</strong>
|
||||
<code>{{ url_for('main.view_file', uri=uri, _external=True) }}</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
{{ content|safe }}
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<a href="{{ url_for('main.upload_file') }}">Upload Another Document</a> |
|
||||
<a href="{{ url_for('main.index') }}">Home</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
.markdown-alert-tip {
|
||||
background-color: rgba(46, 160, 67, 0.15);
|
||||
}
|
||||
|
||||
.markdown-alert-important,
|
||||
.markdown-alert-warning {
|
||||
background-color: rgba(187, 128, 9, 0.15);
|
||||
}
|
||||
|
||||
.markdown-alert-caution {
|
||||
background-color: rgba(248, 81, 73, 0.15);
|
||||
}
|
||||
}
|
||||
</style> -->
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="markdown-body">
|
||||
<h1>{{ filename }}</h1>
|
||||
<p>
|
||||
<strong>Permanent Link:</strong>
|
||||
<code>{{ url_for('main.view_file', uri=uri, _external=True) }}</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="markdown-body">
|
||||
{{ content|safe }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button class="glass" href="{{ url_for('main.upload_file') }}">Upload Another Document</button>
|
||||
<!-- <button class="glass" href="{{ url_for('main.index') }}">Home</button> -->
|
||||
</div>
|
||||
{% endblock %}
|
65
app/utils/utils.py
Normal file
65
app/utils/utils.py
Normal file
|
@ -0,0 +1,65 @@
|
|||
import re
|
||||
from markdown import Markdown
|
||||
from markdown.extensions import Extension
|
||||
from markdown.preprocessors import Preprocessor
|
||||
|
||||
class GithubAdmonitionPreprocessor(Preprocessor):
|
||||
ADMONITION_PATTERN = re.compile(r'^!(?P<type>NOTE|TIP|IMPORTANT|WARNING|CAUTION)\s*(?P<title>.*?)$', re.MULTILINE)
|
||||
|
||||
def run(self, lines):
|
||||
new_lines = []
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
match = self.ADMONITION_PATTERN.match(line)
|
||||
if match:
|
||||
admonition_type = match.group('type').lower()
|
||||
title = match.group('title').strip()
|
||||
|
||||
# Start the div with appropriate classes
|
||||
new_lines.append(f'<div class="markdown-alert markdown-alert-{admonition_type}">')
|
||||
|
||||
# Add the title if provided, otherwise use the type
|
||||
if title:
|
||||
new_lines.append(f'<p class="markdown-alert-title"><svg class="octicon" viewBox="0 0 16 16" version="1.1" width="16" height="16"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>{admonition_type.capitalize()}</p>')
|
||||
else:
|
||||
new_lines.append(f'<p class="markdown-alert-title"><svg class="octicon" viewBox="0 0 16 16" version="1.1" width="16" height="16"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8Zm8-6.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13ZM6.5 7.75A.75.75 0 0 1 7.25 7h1a.75.75 0 0 1 .75.75v2.75h.25a.75.75 0 0 1 0 1.5h-2a.75.75 0 0 1 0-1.5h.25v-2h-.25a.75.75 0 0 1-.75-.75ZM8 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2Z"></path></svg>{admonition_type.capitalize()}</p>')
|
||||
|
||||
# Find the content of the admonition
|
||||
i += 1
|
||||
content_lines = []
|
||||
while i < len(lines) and lines[i].strip() and not self.ADMONITION_PATTERN.match(lines[i]):
|
||||
content_lines.append(lines[i])
|
||||
i += 1
|
||||
|
||||
# Add content and close the div
|
||||
if content_lines:
|
||||
new_lines.append('<p>' + '<br>'.join(content_lines) + '</p>')
|
||||
new_lines.append('</div>')
|
||||
|
||||
# If we reached the end of the content or an empty line, don't increment i again
|
||||
if i >= len(lines) or not lines[i].strip():
|
||||
continue
|
||||
else:
|
||||
new_lines.append(line)
|
||||
i += 1
|
||||
|
||||
return new_lines
|
||||
|
||||
class GithubAdmonitionExtension(Extension):
|
||||
def extendMarkdown(self, md):
|
||||
md.preprocessors.register(GithubAdmonitionPreprocessor(md), 'github_admonition', 175)
|
||||
|
||||
def convert_markdown_to_html(markdown_text):
|
||||
# Create Markdown instance with GitHub-style admonitions
|
||||
md = Markdown(extensions=[
|
||||
'markdown.extensions.fenced_code',
|
||||
'markdown.extensions.tables',
|
||||
'markdown.extensions.nl2br',
|
||||
GithubAdmonitionExtension(),
|
||||
# Add other extensions you need
|
||||
])
|
||||
|
||||
# Convert markdown to HTML
|
||||
html = md.convert(markdown_text)
|
||||
return html
|
Loading…
Add table
Add a link
Reference in a new issue