Add image preview support: Krea-tag parsing with modal, file-explorer preview, skill output endpoint, increase execute timeout to 180s

- SkillEndpoint: GET /api/skills/{slug}/output/{filename} for skill output files
- WorkspaceEndpoint: GET /api/workspace/raw for raw file serving (images)
- File-Explorer: Image preview for png/jpg/gif/webp/svg/bmp/ico
- Chat UI: Parse [Krea: filename] tags into clickable chips with image modal
- ToolService: Increase execute timeout from 30s to 180s for long-running skills
This commit is contained in:
Meleki committed 2026-03-01 20:00:35 +01:00
1 parent cf6caea2dc
commit 9e4ed6dada
7 files changed
+253 -3

No files matched your search

+47
View File
@@ -602,3 +602,50 @@
max-height: 60vh;
overflow: auto;
}
/* Krea Image Refs in Assistant Bubbles */
.krea-ref-chip {
display: inline-flex;
align-items: center;
gap: .3em;
padding: .15em .5em;
background: var(--f2-bg);
border: var(--border-width) solid var(--f2-border);
border-radius: var(--border-radius);
font-size: var(--font-size);
white-space: nowrap;
vertical-align: baseline;
cursor: pointer;
transition: background var(--transition-duration), border-color var(--transition-duration);
}
.krea-ref-chip:hover {
border-color: var(--f3-border);
background: var(--f3-bg);
}
.krea-ref-chip .fa {
color: var(--primary);
}
/* Krea Image Modal */
.krea-modal {
max-width: 90vw;
max-height: 90vh;
}
.krea-modal-body {
display: flex;
align-items: center;
justify-content: center;
padding: var(--box-spacing);
}
.krea-modal-image {
max-width: 100%;
max-height: 75vh;
object-fit: contain;
border-radius: var(--border-radius);
}
+16
View File
@@ -105,6 +105,22 @@
margin: auto;
}
.fe-image-preview {
display: flex;
align-items: center;
justify-content: center;
flex: 1;
padding: 1em;
overflow: auto;
}
.fe-image-preview img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: 4px;
}
/* ── CodeMirror / EasyMDE gemeinsame Wrapper-Basis ── */
+9
View File
@@ -204,6 +204,15 @@ async function feSelectFile(entry, itemEl) {
// Vorherige Editor-Instanz aufräumen
feDestroyEditor();
const imageExts = ['.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.bmp', '.ico'];
if (!entry.isText && imageExts.includes((entry.ext || '').toLowerCase())) {
const rawUrl = _feApiUrl('raw', `path=${encodeURIComponent(entry.path)}`);
main.innerHTML = `<div class="fe-image-preview"><img src="${rawUrl}" alt="${esc(entry.name)}" /></div>`;
footer.hidden = true;
_feState.selectedFile = null;
return;
}
if (!entry.isText) {
main.innerHTML = `<div class="fe-placeholder">${getFileIcon(entry.ext)} ${esc(entry.name)}<br><small>${formatSize(entry.size)} - Vorschau nicht verfügbar</small></div>`;
footer.hidden = true;
+70 -1
View File
@@ -10,7 +10,15 @@ marked.setOptions({ breaks: true, gfm: true });
function renderMarkdown(text) {
if (!text) return '';
return marked.parse(text.trim());
var html = marked.parse(text.trim());
// [Krea: dateiname.png] → klickbarer Bild-Link
html = html.replace(/\[Krea:\s*([^\]]+)\]/g, function(match, filename) {
var fn = filename.trim();
var safeFn = esc(fn);
return '<span class="krea-ref-chip" data-filename="' + safeFn + '" title="Klicken für Vorschau">'
+ '<i class="fa fa-picture-o"></i> ' + safeFn + '</span>';
});
return html;
}
function scrollDown() {
@@ -690,3 +698,64 @@ function removeArchiveLoadButton() {
const btns = chatEl().querySelectorAll('.archive-load-btn:not(.older-messages-btn)');
btns.forEach(b => b.remove());
}
// ── Krea Image Preview Modal ──
function showKreaImageModal(filename) {
var url = '/api/skills/krea-api/output/' + encodeURIComponent(filename);
var overlay = document.createElement('div');
overlay.className = 'sp-overlay krea-overlay';
var modal = document.createElement('div');
modal.className = 'sp-modal krea-modal';
// Header
var header = document.createElement('div');
header.className = 'sp-header';
var title = document.createElement('span');
title.textContent = filename;
var closeBtn = document.createElement('button');
closeBtn.innerHTML = '<i class="fa fa-times"></i>';
closeBtn.className = 'sp-close icon-btn';
header.appendChild(title);
header.appendChild(closeBtn);
// Body
var body = document.createElement('div');
body.className = 'sp-body krea-modal-body';
var img = document.createElement('img');
img.src = url;
img.className = 'krea-modal-image';
img.alt = filename;
img.addEventListener('error', function() {
body.innerHTML = '<div class="sp-loading" style="color:var(--dangerous)">Bild konnte nicht geladen werden</div>';
});
body.appendChild(img);
modal.appendChild(header);
modal.appendChild(body);
overlay.appendChild(modal);
document.body.appendChild(overlay);
// Schließen
var close = function() { overlay.remove(); };
closeBtn.addEventListener('click', close);
overlay.addEventListener('click', function(e) {
if (e.target === overlay) close();
});
var onKey = function(e) {
if (e.key === 'Escape') { close(); document.removeEventListener('keydown', onKey); }
};
document.addEventListener('keydown', onKey);
}
// Event-Delegation für Krea-Chips im Chat
document.addEventListener('click', function(e) {
var chip = e.target.closest('.krea-ref-chip');
if (chip) {
var filename = chip.dataset.filename;
if (filename) showKreaImageModal(filename);
}
});