Ohjeet: screenshot-upload paste & drag-drop + kuva-lightbox
Screenshottien lisääminen ohjeisiin nyt helpompaa: - Ctrl+V / Cmd+V: liitä kuvakaappaus suoraan leikepöydältä editoriin - Drag & drop: raahaa kuvatiedostoja suoraan textarea-editoriin - Upload-placeholder näkyy latauksen aikana (![Ladataan: ...]()) - Vihjeet editorin alla kertovat käytettävissä olevat tavat - Kuva-lightbox: klikkaa kuvaa lukunäkymässä → avautuu isona - Kuvien hover-efekti (zoom-in kursori, kevyt varjo) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -412,8 +412,9 @@
|
|||||||
<span style="flex:1;"></span>
|
<span style="flex:1;"></span>
|
||||||
<button type="button" class="guide-tb-btn" id="btn-guide-preview-toggle">Esikatselu</button>
|
<button type="button" class="guide-tb-btn" id="btn-guide-preview-toggle">Esikatselu</button>
|
||||||
</div>
|
</div>
|
||||||
<textarea id="guide-form-content" rows="20" style="font-family:'SF Mono',Monaco,Consolas,monospace;font-size:0.88rem;line-height:1.6;resize:vertical;min-height:300px;" placeholder="Kirjoita Markdown-muodossa..."></textarea>
|
<textarea id="guide-form-content" rows="20" style="font-family:'SF Mono',Monaco,Consolas,monospace;font-size:0.88rem;line-height:1.6;resize:vertical;min-height:300px;" placeholder="Kirjoita Markdown-muodossa... Vinkki: Liitä kuvakaappaus suoraan Ctrl+V / ⌘V tai raahaa kuvatiedosto tähän"></textarea>
|
||||||
<div id="guide-preview-pane" class="guide-content" style="display:none;padding:1rem;border:2px solid #e0e0e0;border-radius:8px;min-height:300px;background:#fafbfc;"></div>
|
<div id="guide-preview-pane" class="guide-content" style="display:none;padding:1rem;border:2px solid #e0e0e0;border-radius:8px;min-height:300px;background:#fafbfc;"></div>
|
||||||
|
<div style="font-size:0.78rem;color:#999;margin-top:4px;">📋 Liitä screenshot: Ctrl+V | 📁 Raahaa kuva editoriin | 📷 Tai käytä Kuva-nappia</div>
|
||||||
</div>
|
</div>
|
||||||
<label style="display:flex;align-items:center;gap:0.5rem;margin:0.75rem 0;font-size:0.9rem;cursor:pointer;">
|
<label style="display:flex;align-items:center;gap:0.5rem;margin:0.75rem 0;font-size:0.9rem;cursor:pointer;">
|
||||||
<input type="checkbox" id="guide-form-pinned"> Kiinnitetty (näkyy aina listauksen alussa)
|
<input type="checkbox" id="guide-form-pinned"> Kiinnitetty (näkyy aina listauksen alussa)
|
||||||
|
|||||||
104
script.js
104
script.js
@@ -3498,6 +3498,20 @@ let guideCategories = [];
|
|||||||
let currentGuideId = null;
|
let currentGuideId = null;
|
||||||
|
|
||||||
// Markdown-renderöijä (kevyt, ei ulkoisia kirjastoja)
|
// Markdown-renderöijä (kevyt, ei ulkoisia kirjastoja)
|
||||||
|
// Kuva-lightbox ohjeissa
|
||||||
|
function openGuideLightbox(src, alt) {
|
||||||
|
let overlay = document.getElementById('guide-lightbox');
|
||||||
|
if (!overlay) {
|
||||||
|
overlay = document.createElement('div');
|
||||||
|
overlay.id = 'guide-lightbox';
|
||||||
|
overlay.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.85);display:flex;align-items:center;justify-content:center;z-index:10000;cursor:zoom-out;padding:2rem;';
|
||||||
|
overlay.addEventListener('click', () => overlay.style.display = 'none');
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
}
|
||||||
|
overlay.innerHTML = `<img src="${src}" alt="${alt}" style="max-width:95%;max-height:95%;border-radius:8px;box-shadow:0 8px 40px rgba(0,0,0,0.5);">`;
|
||||||
|
overlay.style.display = 'flex';
|
||||||
|
}
|
||||||
|
|
||||||
function renderMarkdown(md) {
|
function renderMarkdown(md) {
|
||||||
if (!md) return '';
|
if (!md) return '';
|
||||||
let html = esc(md);
|
let html = esc(md);
|
||||||
@@ -3513,8 +3527,8 @@ function renderMarkdown(md) {
|
|||||||
html = html.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
|
html = html.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>');
|
||||||
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
html = html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
|
||||||
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
html = html.replace(/\*(.+?)\*/g, '<em>$1</em>');
|
||||||
// Kuvat (ennen linkkejä!)
|
// Kuvat (ennen linkkejä!) — klikkaa avataksesi isompana
|
||||||
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" style="max-width:100%;border-radius:8px;margin:0.5rem 0;">');
|
html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1" class="guide-img" onclick="openGuideLightbox(this.src, this.alt)" title="Klikkaa suurentaaksesi">');
|
||||||
// Linkit
|
// Linkit
|
||||||
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
|
html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
|
||||||
// Lainaukset
|
// Lainaukset
|
||||||
@@ -3746,13 +3760,15 @@ document.getElementById('btn-guide-preview-toggle')?.addEventListener('click', (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Kuva-upload
|
// Kuva-upload: yhteinen upload-funktio
|
||||||
document.getElementById('btn-guide-image')?.addEventListener('click', () => {
|
async function guideUploadImage(file) {
|
||||||
document.getElementById('guide-image-input')?.click();
|
const ta = document.getElementById('guide-form-content');
|
||||||
});
|
if (!ta) return;
|
||||||
document.getElementById('guide-image-input')?.addEventListener('change', async (e) => {
|
const pos = ta.selectionStart;
|
||||||
const file = e.target.files[0];
|
// Näytä upload-placeholder
|
||||||
if (!file) return;
|
const placeholder = `![Ladataan: ${file.name}...]()`;
|
||||||
|
ta.value = ta.value.substring(0, pos) + placeholder + ta.value.substring(ta.selectionEnd);
|
||||||
|
ta.focus();
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('image', file);
|
formData.append('image', file);
|
||||||
try {
|
try {
|
||||||
@@ -3761,16 +3777,70 @@ document.getElementById('guide-image-input')?.addEventListener('change', async (
|
|||||||
});
|
});
|
||||||
const result = await res.json();
|
const result = await res.json();
|
||||||
if (!res.ok) throw new Error(result.error || 'Virhe');
|
if (!res.ok) throw new Error(result.error || 'Virhe');
|
||||||
// Lisää Markdown-kuvatagi editoriin
|
|
||||||
const ta = document.getElementById('guide-form-content');
|
|
||||||
const pos = ta.selectionStart;
|
|
||||||
const mdImg = ``;
|
const mdImg = ``;
|
||||||
ta.value = ta.value.substring(0, pos) + mdImg + ta.value.substring(ta.selectionEnd);
|
ta.value = ta.value.replace(placeholder, mdImg);
|
||||||
ta.focus();
|
} catch (err) {
|
||||||
ta.selectionStart = ta.selectionEnd = pos + mdImg.length;
|
ta.value = ta.value.replace(placeholder, '');
|
||||||
} catch (err) { alert(err.message); }
|
alert('Kuvan lataus epäonnistui: ' + err.message);
|
||||||
e.target.value = ''; // nollaa input
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toolbar-nappi
|
||||||
|
document.getElementById('btn-guide-image')?.addEventListener('click', () => {
|
||||||
|
document.getElementById('guide-image-input')?.click();
|
||||||
});
|
});
|
||||||
|
document.getElementById('guide-image-input')?.addEventListener('change', async (e) => {
|
||||||
|
const file = e.target.files[0];
|
||||||
|
if (file) await guideUploadImage(file);
|
||||||
|
e.target.value = '';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Paste screenshot leikepöydältä (Ctrl+V / Cmd+V)
|
||||||
|
document.getElementById('guide-form-content')?.addEventListener('paste', async (e) => {
|
||||||
|
const items = e.clipboardData?.items;
|
||||||
|
if (!items) return;
|
||||||
|
for (const item of items) {
|
||||||
|
if (item.type.startsWith('image/')) {
|
||||||
|
e.preventDefault();
|
||||||
|
const file = item.getAsFile();
|
||||||
|
if (file) {
|
||||||
|
// Anna tiedostolle nimi aikaleimalla
|
||||||
|
const ext = file.type.split('/')[1] || 'png';
|
||||||
|
const named = new File([file], `screenshot-${Date.now()}.${ext}`, { type: file.type });
|
||||||
|
await guideUploadImage(named);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Drag & drop kuvat editoriin
|
||||||
|
const guideTA = document.getElementById('guide-form-content');
|
||||||
|
if (guideTA) {
|
||||||
|
guideTA.addEventListener('dragover', (e) => {
|
||||||
|
if (e.dataTransfer?.types?.includes('Files')) {
|
||||||
|
e.preventDefault();
|
||||||
|
guideTA.style.borderColor = 'var(--primary-color)';
|
||||||
|
guideTA.style.background = '#f0f7ff';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
guideTA.addEventListener('dragleave', () => {
|
||||||
|
guideTA.style.borderColor = '';
|
||||||
|
guideTA.style.background = '';
|
||||||
|
});
|
||||||
|
guideTA.addEventListener('drop', async (e) => {
|
||||||
|
guideTA.style.borderColor = '';
|
||||||
|
guideTA.style.background = '';
|
||||||
|
const files = e.dataTransfer?.files;
|
||||||
|
if (!files?.length) return;
|
||||||
|
for (const file of files) {
|
||||||
|
if (file.type.startsWith('image/')) {
|
||||||
|
e.preventDefault();
|
||||||
|
await guideUploadImage(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Kategorianhallinta
|
// Kategorianhallinta
|
||||||
document.getElementById('btn-manage-guide-cats')?.addEventListener('click', () => {
|
document.getElementById('btn-manage-guide-cats')?.addEventListener('click', () => {
|
||||||
|
|||||||
@@ -1121,6 +1121,8 @@ span.empty {
|
|||||||
.guide-content hr { border:none; border-top:2px solid #f0f2f5; margin:1.5rem 0; }
|
.guide-content hr { border:none; border-top:2px solid #f0f2f5; margin:1.5rem 0; }
|
||||||
.guide-tb-btn { background:#f0f2f5; border:1px solid #ddd; padding:4px 10px; border-radius:5px; font-size:0.78rem; font-weight:600; cursor:pointer; color:#555; transition:all 0.15s; }
|
.guide-tb-btn { background:#f0f2f5; border:1px solid #ddd; padding:4px 10px; border-radius:5px; font-size:0.78rem; font-weight:600; cursor:pointer; color:#555; transition:all 0.15s; }
|
||||||
.guide-tb-btn:hover { background:var(--primary-color); color:#fff; border-color:var(--primary-color); }
|
.guide-tb-btn:hover { background:var(--primary-color); color:#fff; border-color:var(--primary-color); }
|
||||||
|
.guide-img { max-width:100%; border-radius:8px; margin:0.75rem 0; cursor:zoom-in; transition:transform 0.15s, box-shadow 0.15s; border:1px solid #e8e8e8; }
|
||||||
|
.guide-img:hover { transform:scale(1.01); box-shadow:0 4px 16px rgba(0,0,0,0.12); }
|
||||||
|
|
||||||
/* Role badge */
|
/* Role badge */
|
||||||
.role-badge {
|
.role-badge {
|
||||||
|
|||||||
Reference in New Issue
Block a user