Logo als Pfade, Undo-Fix beim Einfuegen, Drop-Schutz, CSP/HSTS, Datenschutz praezisiert

This commit is contained in:
till committed 2026-09-15 12:24:59 +02:00
1 parent b7a654fc76
commit 916d4b7c7f
5 files changed
+51 -13

No files matched your search

+8
View File
@@ -116,6 +116,14 @@
insbesondere keine Schriftarten und keine Skripte von Content-Delivery-Netzwerken. Eine
Einwilligungsabfrage ist deshalb nicht erforderlich.
</p>
<p>
Eine Ausnahme betrifft die optionale Vorschau: Enthält der von Ihnen eingefuegte Text
Bilder, die auf fremde Server verweisen, lädt Ihr Browser diese Bilder beim Anzeigen der
Vorschau von dort. Dabei erfährt der jeweilige fremde Server Ihre IP-Adresse. Übertragen
wird dabei nichts von Ihrem Text; die Herkunft wird durch die Einstellung
<span class="mono">Referrer-Policy: no-referrer</span> nicht mitgesendet. Wenn Sie das
vermeiden möchten, lassen Sie die Vorschau ausgeschaltet.
</p>
<h2>8. Ihre Rechte</h2>
<p>
+7
View File
@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" role="img" aria-label="clip2md">
<g fill="none" stroke="currentColor" stroke-width="44" stroke-linecap="round" stroke-linejoin="round">
<path d="M118 352V166l86 94 86-94v186"/>
<path d="M384 166v140"/>
<path d="M338 268l46 52 46-52"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 350 B

+6 -4
View File
@@ -1,6 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512">
<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512" role="img" aria-label="clip2md">
<rect width="512" height="512" rx="112" fill="#c14a2b"/>
<text x="50%" y="52%" dominant-baseline="central" text-anchor="middle"
font-family="ui-monospace, Menlo, Consolas, monospace" font-size="230" font-weight="700"
letter-spacing="-12" fill="#fdf6f1">m↓</text>
<g fill="none" stroke="#fdf6f1" stroke-width="44" stroke-linecap="round" stroke-linejoin="round">
<path d="M118 352V166l86 94 86-94v186"/>
<path d="M384 166v140"/>
<path d="M338 268l46 52 46-52"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 404 B

+19 -7
View File
@@ -472,7 +472,7 @@
pattern: LS.get('pattern', 'title')
};
var filenameEdited = false, forceRaw = false, toastTimer = null, previewOn = LS.get('preview', false);
var filenameEdited = false, forceRaw = false, forceRawTimer = null, toastTimer = null, previewOn = LS.get('preview', false);
// ---------- Turndown (rebuilt on settings change) ----------
var td;
@@ -497,7 +497,7 @@
var UML={ 'ä':'ae','ö':'oe','ü':'ue','ß':'ss','Ä':'ae','Ö':'oe','Ü':'ue' };
function slugify(s){ if(!s) return '';
s=s.replace(/[äöüßÄÖÜ]/g,function(c){return UML[c]||c;}).normalize('NFKD').replace(/[̀-ͯ]/g,'');
s=s.replace(/[äöüßÄÖÜ]/g,function(c){return UML[c]||c;}).normalize('NFKD').replace(/[\u0300-\u036f]/g,'');
return s.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'').slice(0,60).replace(/-+$/g,''); }
function deriveTitle(md){ var L=md.split(/\r?\n/), i, m, t;
for(i=0;i<L.length;i++){ m=L[i].match(/^\s{0,3}#{1,6}\s+(.+?)\s*#*\s*$/); if(m&&m[1].trim()) return m[1].trim(); }
@@ -531,7 +531,7 @@
// ---------- Preview ----------
var pvTimer=null;
function escapeHtml(s){ return s.replace(/[&<>"]/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[c]; }); }
function escapeHtml(s){ return s.replace(/[&<>"']/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]; }); }
function renderPreview(){ if(!previewOn) return;
var md=editor.value, fmHtml='';
// Show YAML front matter as a compact metadata block, not as body text.
@@ -564,8 +564,15 @@
forceRaw=false; if(out==null) return;
e.preventDefault(); insertAtCursor(out); showSource(key,ok,true); refreshAll(true);
});
function insertAtCursor(str){ var s=editor.selectionStart,en=editor.selectionEnd;
editor.value=editor.value.slice(0,s)+str+editor.value.slice(en); var p=s+str.length; editor.selectionStart=editor.selectionEnd=p; }
function insertAtCursor(str){
// execCommand haelt den nativen Undo-Stack der Textarea intakt (Cmd+Z nach dem Einfuegen).
editor.focus();
var ok=false;
try { ok = document.execCommand('insertText', false, str); } catch(e){ ok=false; }
if(ok) return;
var s=editor.selectionStart,en=editor.selectionEnd;
editor.value=editor.value.slice(0,s)+str+editor.value.slice(en);
var p=s+str.length; editor.selectionStart=editor.selectionEnd=p; }
editor.addEventListener('input', function(){ hideSource(); refreshAll(false); });
@@ -575,10 +582,15 @@
editor.addEventListener('drop', function(e){
var f=e.dataTransfer&&e.dataTransfer.files&&e.dataTransfer.files[0]; main.classList.remove('drag'); if(!f) return;
e.preventDefault(); var r=new FileReader();
if(f.size > 10*1024*1024){ toast(lang==='de'?'Datei zu gross (max. 10 MB)':'File too large (max 10 MB)', true); return; }
if(editor.value.trim() && !confirm(lang==='de'?'Editor-Inhalt durch die Datei ersetzen?':'Replace the editor content with this file?')) return;
r.onerror=function(){ toast(lang==='de'?'Datei nicht lesbar':'Could not read file', true); };
r.onload=function(){ var txt=String(r.result||''); var name=f.name.toLowerCase();
if(/\.html?$/.test(name)){ try{ txt=td.turndown(txt).replace(/\n{3,}/g,'\n\n').trim(); showSource('src_conv',true,true); }catch(_){ showSource('src_fail',false,true);} }
else { showSource('src_raw',false,true); }
editor.value=txt; if(!filenameEdited && /\.(md|markdown|txt|html?)$/.test(name)){ } refreshAll(true); toast(t('loaded')); };
editor.value=txt;
if(!filenameEdited){ var base=f.name.replace(/\.[^.]+$/,''); if(base){ filename.value=slugify(base)||computeFilename(txt); } }
refreshAll(true); toast(t('loaded')); };
r.readAsText(f);
});
@@ -608,7 +620,7 @@
// ---------- Keyboard ----------
document.addEventListener('keydown', function(e){ var mod=e.metaKey||e.ctrlKey;
if(mod && !e.shiftKey && !e.altKey && (e.key==='s'||e.key==='S')){ e.preventDefault(); save(); return; }
if(mod && e.shiftKey && (e.key==='v'||e.key==='V')){ forceRaw=true; }
if(mod && e.shiftKey && (e.key==='v'||e.key==='V')){ forceRaw=true; clearTimeout(forceRawTimer); forceRawTimer=setTimeout(function(){ forceRaw=false; },3000); }
if(e.key==='Escape'){ closeSettings(); } });
// ---------- Preview toggle ----------
+11 -2
View File
@@ -40,8 +40,17 @@ server {
try_files $uri $uri.html $uri/ /index.html;
}
# Minimal hardening headers.
# Hardening headers.
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Permissions-Policy "geolocation=(), camera=(), microphone=(), payment=(), usb=(), interest-cohort=()" always;
# Content Security Policy.
# connect-src 'none' ist hier das eigentliche Versprechen: der Browser selbst
# unterbindet jede fetch/XHR/WebSocket-Verbindung. "Nothing is uploaded" ist damit
# nicht nur behauptet, sondern erzwungen. img-src erlaubt https, damit Bilder aus
# eingefuegtem Markdown in der Vorschau sichtbar bleiben.
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self'; connect-src 'none'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'; manifest-src 'self'; worker-src 'self'" always;
}