355 lines
15 KiB
HTML
355 lines
15 KiB
HTML
{% extends 'base.html' %}
|
|
|
|
{% block title %}Neues Thema - {{ category.title }}{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
.markdown-preview {
|
|
min-height: 200px;
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
line-height: 1.6;
|
|
}
|
|
.markdown-preview p {
|
|
margin-bottom: 1rem;
|
|
}
|
|
.markdown-preview h1, .markdown-preview h2, .markdown-preview h3,
|
|
.markdown-preview h4, .markdown-preview h5, .markdown-preview h6 {
|
|
margin-top: 1.5rem;
|
|
margin-bottom: 0.75rem;
|
|
font-weight: 600;
|
|
}
|
|
.markdown-preview h1 { font-size: 1.8rem; }
|
|
.markdown-preview h2 { font-size: 1.5rem; }
|
|
.markdown-preview h3 { font-size: 1.3rem; }
|
|
.markdown-preview h4 { font-size: 1.1rem; }
|
|
.markdown-preview ul, .markdown-preview ol {
|
|
margin-left: 1.5rem;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.markdown-preview ul { list-style-type: disc; }
|
|
.markdown-preview ol { list-style-type: decimal; }
|
|
.markdown-preview pre {
|
|
background-color: rgba(0,0,0,0.05);
|
|
padding: 1rem;
|
|
border-radius: 0.5rem;
|
|
overflow-x: auto;
|
|
margin: 1rem 0;
|
|
}
|
|
.markdown-preview code {
|
|
font-family: 'JetBrains Mono', monospace;
|
|
font-size: 0.9em;
|
|
padding: 0.1em 0.3em;
|
|
border-radius: 0.3em;
|
|
background-color: rgba(0,0,0,0.05);
|
|
}
|
|
.markdown-preview pre code {
|
|
padding: 0;
|
|
background-color: transparent;
|
|
}
|
|
.markdown-preview blockquote {
|
|
border-left: 4px solid;
|
|
padding-left: 1rem;
|
|
margin-left: 0;
|
|
margin-right: 0;
|
|
margin-bottom: 1rem;
|
|
opacity: 0.8;
|
|
}
|
|
.dark .markdown-preview code {
|
|
background-color: rgba(255,255,255,0.1);
|
|
}
|
|
.dark .markdown-preview blockquote {
|
|
border-color: rgba(255,255,255,0.2);
|
|
}
|
|
.node-mention {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
background-color: rgba(109, 40, 217, 0.1);
|
|
color: #6d28d9;
|
|
border-radius: 4px;
|
|
padding: 1px 6px;
|
|
font-size: 0.9em;
|
|
margin: 0 2px;
|
|
font-weight: 500;
|
|
}
|
|
.dark .node-mention {
|
|
background-color: rgba(167, 139, 250, 0.2);
|
|
color: #a78bfa;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container mx-auto px-4 py-8">
|
|
<!-- Breadcrumb Navigation -->
|
|
<div class="mb-6 flex items-center text-sm">
|
|
<a href="{{ url_for('community') }}" class="opacity-75 hover:opacity-100 transition-opacity">
|
|
<i class="fas fa-home mr-1"></i> Forum
|
|
</a>
|
|
<span class="mx-2 opacity-50">/</span>
|
|
<a href="{{ url_for('forum_category', category_id=category.id) }}" class="opacity-75 hover:opacity-100 transition-opacity">
|
|
{{ category.title }}
|
|
</a>
|
|
<span class="mx-2 opacity-50">/</span>
|
|
<span class="font-medium">Neues Thema</span>
|
|
</div>
|
|
|
|
<!-- Formular-Header -->
|
|
<div class="mb-6">
|
|
<h1 class="text-2xl font-bold mb-2">Neues Thema erstellen</h1>
|
|
<p class="opacity-75">in der Kategorie <span class="font-medium">{{ category.title }}</span></p>
|
|
</div>
|
|
|
|
<!-- Formular -->
|
|
<div class="mb-8 rounded-xl overflow-hidden"
|
|
x-bind:class="darkMode ? 'bg-gray-800/60 border border-white/10' : 'bg-white border border-gray-200'">
|
|
<div class="p-4 border-b font-medium" x-bind:class="darkMode ? 'border-white/10' : 'border-gray-200'">
|
|
<i class="fas fa-plus-circle mr-2"></i>
|
|
Neues Thema
|
|
</div>
|
|
<div class="p-6">
|
|
<form action="{{ url_for('new_post', category_id=category.id) }}" method="POST" x-data="{
|
|
title: '',
|
|
content: '',
|
|
showPreview: false,
|
|
previewHtml: '',
|
|
|
|
updatePreview() {
|
|
// Verarbeite den Inhalt
|
|
if (this.content.trim() === '') {
|
|
this.previewHtml = '<div class=\'opacity-50 italic\'>Die Vorschau wird hier angezeigt...</div>';
|
|
return;
|
|
}
|
|
|
|
// Verarbeite Markdown
|
|
let html = marked.parse(this.content);
|
|
|
|
// Ersetze @Knotenname mit entsprechenden Links
|
|
html = html.replace(/@([a-zA-Z0-9äöüÄÖÜß_-]+)/g, '<span class=\'node-mention\'><i class=\'fas fa-diagram-project fa-xs mr-1\'></i>$1</span>');
|
|
|
|
this.previewHtml = html;
|
|
}
|
|
}">
|
|
<div class="mb-6">
|
|
<label for="title" class="block mb-2 font-medium">Titel des Themas</label>
|
|
<div class="rounded-lg overflow-hidden"
|
|
x-bind:class="darkMode ? 'border border-white/20' : 'border border-gray-300'">
|
|
<input type="text" id="title" name="title"
|
|
class="w-full px-4 py-3"
|
|
x-bind:class="darkMode
|
|
? 'bg-gray-700/50 text-white placeholder-gray-400 focus:border-indigo-500'
|
|
: 'bg-white text-gray-700 placeholder-gray-400 focus:border-indigo-500'"
|
|
placeholder="Ein prägnanter Titel für dein Thema"
|
|
x-model="title"
|
|
required>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-6">
|
|
<div class="flex justify-between items-center mb-2">
|
|
<label for="content" class="font-medium">Inhalt</label>
|
|
<div class="flex space-x-2">
|
|
<button type="button"
|
|
class="px-3 py-1 rounded text-sm flex items-center"
|
|
x-bind:class="darkMode
|
|
? 'bg-gray-700 hover:bg-gray-600 text-white'
|
|
: 'bg-gray-200 hover:bg-gray-300 text-gray-700'"
|
|
@click="showPreview = false"
|
|
x-bind:disabled="!showPreview"
|
|
x-bind:class="{'opacity-50': !showPreview}">
|
|
<i class="fas fa-edit mr-1"></i> Bearbeiten
|
|
</button>
|
|
<button type="button"
|
|
class="px-3 py-1 rounded text-sm flex items-center"
|
|
x-bind:class="darkMode
|
|
? 'bg-gray-700 hover:bg-gray-600 text-white'
|
|
: 'bg-gray-200 hover:bg-gray-300 text-gray-700'"
|
|
@click="updatePreview(); showPreview = true"
|
|
x-bind:disabled="showPreview"
|
|
x-bind:class="{'opacity-50': showPreview}">
|
|
<i class="fas fa-eye mr-1"></i> Vorschau
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Editor -->
|
|
<div class="rounded-lg overflow-hidden mb-2"
|
|
x-bind:class="darkMode ? 'border border-white/20' : 'border border-gray-300'"
|
|
x-show="!showPreview">
|
|
<textarea id="content" name="content" rows="12"
|
|
class="w-full p-3 resize-y"
|
|
x-bind:class="darkMode
|
|
? 'bg-gray-700/50 text-white placeholder-gray-400 focus:border-indigo-500'
|
|
: 'bg-white text-gray-700 placeholder-gray-400 focus:border-indigo-500'"
|
|
placeholder="Schreibe deinen Beitrag hier (unterstützt Markdown und @Knotenname-Erwähnungen)..."
|
|
x-model="content"
|
|
required></textarea>
|
|
</div>
|
|
|
|
<!-- Preview -->
|
|
<div class="rounded-lg overflow-hidden mb-2 p-4 markdown-preview"
|
|
x-bind:class="darkMode
|
|
? 'border border-white/20 bg-gray-700/30'
|
|
: 'border border-gray-300 bg-gray-50'"
|
|
x-show="showPreview"
|
|
x-html="previewHtml">
|
|
</div>
|
|
|
|
<!-- Markdown-Hilfsmittel -->
|
|
<div class="mb-4" x-show="!showPreview">
|
|
<div class="text-xs opacity-70">
|
|
<p>Du kannst Knotenpunkte der Mindmap durch <code>@Knotenname</code> verlinken.</p>
|
|
<p>Dieser Editor unterstützt Markdown-Formatierung:</p>
|
|
<div class="flex flex-wrap gap-2 mt-1">
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format="**" data-before="" data-after="" title="Fett">
|
|
<i class="fas fa-bold"></i>
|
|
</button>
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format="*" data-before="" data-after="" title="Kursiv">
|
|
<i class="fas fa-italic"></i>
|
|
</button>
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format="`" data-before="" data-after="" title="Code">
|
|
<i class="fas fa-code"></i>
|
|
</button>
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format="[Link-Text](URL)" data-before="" data-after="" title="Link">
|
|
<i class="fas fa-link"></i>
|
|
</button>
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format="\n```\nCode-Block\n```" data-before="" data-after="" title="Code-Block">
|
|
<i class="fas fa-file-code"></i>
|
|
</button>
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format=">" data-before="" data-after="" title="Zitat">
|
|
<i class="fas fa-quote-right"></i>
|
|
</button>
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format="- " data-before="" data-after="" title="Liste">
|
|
<i class="fas fa-list-ul"></i>
|
|
</button>
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format="1. " data-before="" data-after="" title="Nummerierte Liste">
|
|
<i class="fas fa-list-ol"></i>
|
|
</button>
|
|
<button type="button" class="markdown-button px-2 py-1 rounded text-xs" data-format="# " data-before="" data-after="" title="Überschrift">
|
|
<i class="fas fa-heading"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex justify-between items-center">
|
|
<a href="{{ url_for('forum_category', category_id=category.id) }}"
|
|
class="px-5 py-2.5 rounded-lg transition-all duration-300 flex items-center"
|
|
x-bind:class="darkMode
|
|
? 'bg-gray-700 hover:bg-gray-600 text-white'
|
|
: 'bg-gray-200 hover:bg-gray-300 text-gray-700'">
|
|
Abbrechen
|
|
</a>
|
|
<button type="submit"
|
|
class="px-5 py-2.5 rounded-lg transition-all duration-300 flex items-center"
|
|
x-bind:class="darkMode
|
|
? 'bg-indigo-700 hover:bg-indigo-600 text-white'
|
|
: 'bg-indigo-500 hover:bg-indigo-600 text-white'">
|
|
<i class="fas fa-paper-plane mr-2"></i>
|
|
Thema erstellen
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Link zur Mindmap -->
|
|
<div class="rounded-xl p-5 mb-4 flex items-center"
|
|
x-bind:class="darkMode ? 'bg-purple-900/20 border border-purple-800/30' : 'bg-purple-50 border border-purple-100'">
|
|
<div class="text-3xl mr-4 opacity-80">
|
|
<i class="fas fa-diagram-project" style="color: {{ category.node.color_code }}"></i>
|
|
</div>
|
|
<div>
|
|
<h3 class="font-medium mb-1">Mindmap-Knotenpunkt: {{ category.node.name }}</h3>
|
|
<p class="text-sm opacity-75">Dieser Diskussionsbereich ist mit dem Mindmap-Knotenpunkt "{{ category.node.name }}" verknüpft.</p>
|
|
</div>
|
|
<div class="ml-auto">
|
|
<a href="{{ url_for('mindmap') }}"
|
|
class="px-4 py-2 rounded-lg inline-block text-sm transition-all"
|
|
x-bind:class="darkMode
|
|
? 'bg-purple-800/60 hover:bg-purple-700/60 text-white'
|
|
: 'bg-white hover:bg-purple-100 text-purple-800 border border-purple-200'">
|
|
Zur Mindmap
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Markdown-Buttons für den Beitragseditor
|
|
document.querySelectorAll('.markdown-button').forEach(button => {
|
|
button.addEventListener('click', function() {
|
|
const textarea = document.getElementById('content');
|
|
const format = this.dataset.format;
|
|
const before = this.dataset.before || '';
|
|
const after = this.dataset.after || '';
|
|
|
|
// Hole die aktuelle Auswahl
|
|
const start = textarea.selectionStart;
|
|
const end = textarea.selectionEnd;
|
|
const selection = textarea.value.substring(start, end);
|
|
|
|
// Wende die Formatierung an
|
|
let formattedText;
|
|
if (format.includes('\n')) {
|
|
// Für Formate mit Zeilenumbrüchen (z.B. Code-Blöcke)
|
|
formattedText = format.replace('Code-Block', selection || 'Code-Block');
|
|
} else if (format.includes('[Link-Text](URL)')) {
|
|
formattedText = format.replace('Link-Text', selection || 'Link-Text');
|
|
} else if (format === '- ' || format === '1. ' || format === '# ' || format === '> ') {
|
|
// Für Listen und Überschriften: am Anfang der Zeile einfügen
|
|
const beforeSelection = textarea.value.substring(0, start);
|
|
const afterSelection = textarea.value.substring(end);
|
|
|
|
// Finde den Anfang der aktuellen Zeile
|
|
const lastNewline = beforeSelection.lastIndexOf('\n');
|
|
const lineStart = lastNewline === -1 ? 0 : lastNewline + 1;
|
|
|
|
// Füge das Format am Zeilenanfang ein
|
|
formattedText = beforeSelection.substring(0, lineStart) +
|
|
format +
|
|
beforeSelection.substring(lineStart) +
|
|
selection +
|
|
afterSelection;
|
|
|
|
// Setze die neue Cursor-Position
|
|
const newCursorPos = end + format.length;
|
|
textarea.value = formattedText;
|
|
textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
|
|
// Alpine.js Model aktualisieren
|
|
textarea.dispatchEvent(new Event('input'));
|
|
return; // Früher zurückkehren, da wir die Formatierung bereits angewendet haben
|
|
} else {
|
|
// Für einfache Formatierungen wie fett, kursiv, Code
|
|
formattedText = before + format + selection + format + after;
|
|
}
|
|
|
|
// Ersetze den Text
|
|
textarea.value = textarea.value.substring(0, start) + formattedText + textarea.value.substring(end);
|
|
|
|
// Setze den Fokus zurück auf das Textarea
|
|
textarea.focus();
|
|
|
|
// Alpine.js Model aktualisieren
|
|
textarea.dispatchEvent(new Event('input'));
|
|
|
|
// Setze die Auswahl neu, wenn es eine Auswahl gab
|
|
if (selection) {
|
|
const newStart = start + before.length + format.length;
|
|
const newEnd = newStart + selection.length;
|
|
textarea.setSelectionRange(newStart, newEnd);
|
|
} else {
|
|
// Setze den Cursor in die Mitte von **|** oder `|`
|
|
const newCursorPos = start + before.length + format.length;
|
|
textarea.setSelectionRange(newCursorPos, newCursorPos);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
{% endblock %} |