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:
@@ -40,6 +40,29 @@
|
|||||||
.documentation-content .doc-cell-blue { background: #dbeafe !important; color: #212529; }
|
.documentation-content .doc-cell-blue { background: #dbeafe !important; color: #212529; }
|
||||||
.documentation-content .doc-cell-middle { vertical-align: middle !important; }
|
.documentation-content .doc-cell-middle { vertical-align: middle !important; }
|
||||||
.documentation-content p { max-width: 90rem; }
|
.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-content table { cursor: zoom-in; }
|
||||||
#documentation-viewer.documentation-page-focus {
|
#documentation-viewer.documentation-page-focus {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@@ -120,6 +143,36 @@
|
|||||||
openViewer();
|
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 => {
|
document.addEventListener('keydown', event => {
|
||||||
if (event.key === 'Escape' && viewer.classList.contains('documentation-page-focus')) closeViewer();
|
if (event.key === 'Escape' && viewer.classList.contains('documentation-page-focus')) closeViewer();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -31,10 +31,10 @@
|
|||||||
license_key: 'gpl',
|
license_key: 'gpl',
|
||||||
height: 650,
|
height: 650,
|
||||||
menubar: 'file edit view insert format tools table help',
|
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: [
|
toolbar: [
|
||||||
'undo redo | blocks styles fontfamily fontsize | bold italic underline strikethrough | forecolor backcolor',
|
'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',
|
toolbar_mode: 'sliding',
|
||||||
table_toolbar: 'tableprops tablecellprops tablecellcolor | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol | tablemergecells tablesplitcells | tabledelete',
|
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-red { background: #ffe3e3 !important; }
|
||||||
.doc-cell-blue { background: #dbeafe !important; }
|
.doc-cell-blue { background: #dbeafe !important; }
|
||||||
.doc-cell-middle { vertical-align: middle !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,
|
browser_spellcheck: true,
|
||||||
contextmenu: 'link image table',
|
contextmenu: 'link image table codesample',
|
||||||
promotion: false,
|
promotion: false,
|
||||||
branding: false,
|
branding: false,
|
||||||
convert_unsafe_embeds: true,
|
convert_unsafe_embeds: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user