add clickable links, media embeds and lightbox to chat

- URLs are now clickable (open in new tab)
- Images (jpg/png/gif/webp/svg) embed inline, click for fullscreen
- Videos (mp4/webm) embed inline with controls, click for fullscreen
- PDFs and other files open in new tab (browser handles download)
- Fullscreen lightbox closes on click or Escape

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
duffyduck 2026-03-15 22:10:50 +01:00
parent a58b5073c6
commit 5b91975061
1 changed files with 55 additions and 2 deletions

View File

@ -64,11 +64,19 @@
.chat-box { background: #080810; border: 1px solid #1E1E2E; border-radius: 6px;
min-height: 120px; max-height: 250px; overflow-y: auto; padding: 8px; margin-bottom: 8px; }
.chat-msg { margin-bottom: 6px; padding: 6px 10px; border-radius: 6px; font-size: 13px; }
.chat-msg { margin-bottom: 6px; padding: 6px 10px; border-radius: 6px; font-size: 13px; line-height: 1.5; word-wrap: break-word; }
.chat-msg.sent { background: #0096FF; color: #fff; margin-left: 20%; text-align: right; }
.chat-msg.received { background: #1E1E2E; margin-right: 20%; }
.chat-msg.error { background: #3B1010; color: #FF6B6B; }
.chat-msg .meta { font-size: 10px; color: rgba(255,255,255,0.4); margin-top: 2px; }
.chat-msg a { color: #66BBFF; text-decoration: underline; }
.chat-msg.sent a { color: #CCEEFF; }
.chat-msg .chat-media { max-width: 100%; max-height: 200px; border-radius: 4px; margin-top: 4px; cursor: pointer; display: block; }
.chat-msg .chat-media:hover { opacity: 0.85; }
.lightbox-overlay { display:none; position:fixed; top:0; left:0; right:0; bottom:0; background:rgba(0,0,0,0.92);
z-index:2000; justify-content:center; align-items:center; cursor:pointer; }
.lightbox-overlay.open { display:flex; }
.lightbox-overlay img, .lightbox-overlay video { max-width:95vw; max-height:95vh; border-radius:8px; }
.input-row { display: flex; gap: 6px; }
.input-row input { flex: 1; background: #1E1E2E; border: 1px solid #333; border-radius: 6px;
@ -368,6 +376,9 @@
</div>
</div>
<!-- Lightbox fuer Medien -->
<div class="lightbox-overlay" id="lightbox" onclick="closeLightbox()"></div>
<script>
const chatBox = document.getElementById('chat-box');
const pauseHint = document.getElementById('pause-hint');
@ -803,14 +814,51 @@
if (autoScroll[tab]) box.scrollTop = box.scrollHeight;
}
const MEDIA_IMG = /\.(jpe?g|png|gif|webp|svg|bmp|ico)$/i;
const MEDIA_VID = /\.(mp4|webm|ogg|mov)$/i;
const FILE_PDF = /\.pdf$/i;
function linkifyText(escaped) {
// URLs in escaped HTML erkennen und ersetzen
return escaped.replace(/(https?:\/\/[^\s<&]+)/g, function(url) {
const decoded = url.replace(/&amp;/g, '&');
const pathname = new URL(decoded).pathname;
if (MEDIA_IMG.test(pathname)) {
return `<a href="${url}" target="_blank">${url}</a><img src="${url}" class="chat-media" onclick="event.stopPropagation();openLightbox('img','${url}')">`;
}
if (MEDIA_VID.test(pathname)) {
return `<a href="${url}" target="_blank">${url}</a><video src="${url}" class="chat-media" controls onclick="event.stopPropagation();openLightbox('video','${url}')"></video>`;
}
return `<a href="${url}" target="_blank" rel="noopener">${url}</a>`;
});
}
function addChat(type, text, meta) {
const el = document.createElement('div');
el.className = `chat-msg ${type}`;
el.innerHTML = `${escapeHtml(text)}<div class="meta">${escapeHtml(meta)} — ${new Date().toLocaleTimeString('de-DE')}</div>`;
const escaped = escapeHtml(text);
const linked = linkifyText(escaped);
el.innerHTML = `${linked}<div class="meta">${escapeHtml(meta)} — ${new Date().toLocaleTimeString('de-DE')}</div>`;
chatBox.appendChild(el);
chatBox.scrollTop = chatBox.scrollHeight;
}
function openLightbox(mediaType, url) {
const lb = document.getElementById('lightbox');
if (mediaType === 'video') {
lb.innerHTML = `<video src="${url}" controls autoplay style="max-width:95vw;max-height:95vh;border-radius:8px;" onclick="event.stopPropagation()"></video>`;
} else {
lb.innerHTML = `<img src="${url}" style="max-width:95vw;max-height:95vh;border-radius:8px;">`;
}
lb.classList.add('open');
}
function closeLightbox() {
const lb = document.getElementById('lightbox');
lb.classList.remove('open');
lb.innerHTML = '';
}
function showDockerLogs(msg) {
const tab = msg.tab;
const box = logBoxes[tab];
@ -867,6 +915,11 @@
if (e.key === 'Enter') testGateway();
});
// Escape schliesst Lightbox
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeLightbox();
});
// ── ARIA Live-Ansicht (SSH + Desktop) ──────────────────
let liveSshTerm = null;