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:
+55
-2
@@ -64,11 +64,19 @@
|
|||||||
|
|
||||||
.chat-box { background: #080810; border: 1px solid #1E1E2E; border-radius: 6px;
|
.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; }
|
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.sent { background: #0096FF; color: #fff; margin-left: 20%; text-align: right; }
|
||||||
.chat-msg.received { background: #1E1E2E; margin-right: 20%; }
|
.chat-msg.received { background: #1E1E2E; margin-right: 20%; }
|
||||||
.chat-msg.error { background: #3B1010; color: #FF6B6B; }
|
.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 .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 { display: flex; gap: 6px; }
|
||||||
.input-row input { flex: 1; background: #1E1E2E; border: 1px solid #333; border-radius: 6px;
|
.input-row input { flex: 1; background: #1E1E2E; border: 1px solid #333; border-radius: 6px;
|
||||||
@@ -368,6 +376,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Lightbox fuer Medien -->
|
||||||
|
<div class="lightbox-overlay" id="lightbox" onclick="closeLightbox()"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const chatBox = document.getElementById('chat-box');
|
const chatBox = document.getElementById('chat-box');
|
||||||
const pauseHint = document.getElementById('pause-hint');
|
const pauseHint = document.getElementById('pause-hint');
|
||||||
@@ -803,14 +814,51 @@
|
|||||||
if (autoScroll[tab]) box.scrollTop = box.scrollHeight;
|
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(/&/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) {
|
function addChat(type, text, meta) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.className = `chat-msg ${type}`;
|
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.appendChild(el);
|
||||||
chatBox.scrollTop = chatBox.scrollHeight;
|
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) {
|
function showDockerLogs(msg) {
|
||||||
const tab = msg.tab;
|
const tab = msg.tab;
|
||||||
const box = logBoxes[tab];
|
const box = logBoxes[tab];
|
||||||
@@ -867,6 +915,11 @@
|
|||||||
if (e.key === 'Enter') testGateway();
|
if (e.key === 'Enter') testGateway();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Escape schliesst Lightbox
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Escape') closeLightbox();
|
||||||
|
});
|
||||||
|
|
||||||
// ── ARIA Live-Ansicht (SSH + Desktop) ──────────────────
|
// ── ARIA Live-Ansicht (SSH + Desktop) ──────────────────
|
||||||
|
|
||||||
let liveSshTerm = null;
|
let liveSshTerm = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user