feat: Code-Bloecke mit Kopieren-Button und Code-Beispiel-Editor

Im Editor gibt es jetzt das TinyMCE-Plugin "codesample", um Code-
Bloecke ohne Markdown/HTML einzufuegen. In der Ansicht erhalten
Code-Bloecke ein einheitliches dunkles Layout, eine gut sichtbare
Textmarkierung (eingefuegte Inline-Hintergruende werden ignoriert)
und einen Kopieren-Button mit Fallback fuer Umgebungen ohne
Clipboard-API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 09:13:12 +02:00
co-authored by Claude Opus 5.5
parent dce3d83fd2
commit d426f50f41
2 changed files with 71 additions and 3 deletions
@@ -40,6 +40,29 @@
.documentation-content .doc-cell-blue { background: #dbeafe !important; color: #212529; }
.documentation-content .doc-cell-middle { vertical-align: middle !important; }
.documentation-content p { max-width: 90rem; }
.documentation-content .doc-code-block { position: relative; margin: 0 0 1rem; }
.documentation-content pre {
margin: 0;
padding: .85rem 4.5rem .85rem 1rem;
background: #1e1e1e !important;
color: #e9ecef !important;
border-radius: 6px;
font-family: Consolas, 'Courier New', monospace;
font-size: 10pt;
line-height: 1.5;
white-space: pre-wrap;
overflow-x: auto;
}
.documentation-content pre * { background: transparent !important; font-family: inherit !important; }
.documentation-content pre ::selection,
.documentation-content pre::selection { background: #4c8bf5 !important; color: #fff !important; }
.documentation-content .doc-code-copy {
position: absolute; top: .4rem; right: .4rem;
padding: .15rem .5rem; font-size: .75rem;
color: #e9ecef; background: rgba(255,255,255,.1);
border: 1px solid rgba(255,255,255,.25); border-radius: 4px;
}
.documentation-content .doc-code-copy:hover { background: rgba(255,255,255,.2); }
.documentation-content table { cursor: zoom-in; }
#documentation-viewer.documentation-page-focus {
position: fixed;
@@ -120,6 +143,36 @@
openViewer();
});
});
viewer.querySelectorAll('.documentation-content pre').forEach(pre => {
const wrapper = document.createElement('div');
wrapper.className = 'doc-code-block';
pre.parentNode.insertBefore(wrapper, pre);
wrapper.appendChild(pre);
const copyButton = document.createElement('button');
copyButton.type = 'button';
copyButton.className = 'doc-code-copy';
copyButton.innerHTML = '<i class="mdi mdi-content-copy"></i> Kopieren';
copyButton.addEventListener('click', async () => {
const text = pre.innerText.replace(/
$/, '');
try {
await navigator.clipboard.writeText(text);
} catch (error) {
// Fallback for plain-HTTP deployments without Clipboard API.
const area = document.createElement('textarea');
area.value = text;
area.style.position = 'fixed';
area.style.opacity = '0';
document.body.appendChild(area);
area.select();
document.execCommand('copy');
area.remove();
}
copyButton.innerHTML = '<i class="mdi mdi-check"></i> Kopiert';
setTimeout(() => { copyButton.innerHTML = '<i class="mdi mdi-content-copy"></i> Kopieren'; }, 1500);
});
wrapper.appendChild(copyButton);
});
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && viewer.classList.contains('documentation-page-focus')) closeViewer();
});
@@ -31,10 +31,10 @@
license_key: 'gpl',
height: 650,
menubar: 'file edit view insert format tools table help',
plugins: 'advlist anchor autolink charmap code fullscreen image link lists media preview searchreplace table visualblocks wordcount',
plugins: 'advlist anchor autolink charmap code codesample fullscreen image link lists media preview searchreplace table visualblocks wordcount',
toolbar: [
'undo redo | blocks styles fontfamily fontsize | bold italic underline strikethrough | forecolor backcolor',
'alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table tablecellcolor link image media | removeformat code fullscreen'
'alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | table tablecellcolor link image media codesample | removeformat code fullscreen'
],
toolbar_mode: 'sliding',
table_toolbar: 'tableprops tablecellprops tablecellcolor | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol | tablemergecells tablesplitcells | tabledelete',
@@ -88,9 +88,24 @@
.doc-cell-red { background: #ffe3e3 !important; }
.doc-cell-blue { background: #dbeafe !important; }
.doc-cell-middle { vertical-align: middle !important; }
pre { background: #1e1e1e; color: #e9ecef; padding: .75rem 1rem; border-radius: 6px; font-family: Consolas, 'Courier New', monospace; font-size: 10pt; white-space: pre-wrap; }
pre ::selection, pre::selection { background: #4c8bf5; color: #fff; }
`,
codesample_global_prismjs: false,
codesample_languages: [
{text: 'Bash / Shell', value: 'bash'},
{text: 'PowerShell', value: 'powershell'},
{text: 'Cisco / Netzwerk-Konfiguration', value: 'none'},
{text: 'Python', value: 'python'},
{text: 'JSON', value: 'json'},
{text: 'YAML', value: 'yaml'},
{text: 'SQL', value: 'sql'},
{text: 'HTML/XML', value: 'markup'},
{text: 'JavaScript', value: 'javascript'},
{text: 'Nur Text', value: 'plaintext'}
],
browser_spellcheck: true,
contextmenu: 'link image table',
contextmenu: 'link image table codesample',
promotion: false,
branding: false,
convert_unsafe_embeds: true,