A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
235 lines
5.4 KiB
JavaScript
235 lines
5.4 KiB
JavaScript
/**
|
|
* BreakPilot Studio - Lightbox Module
|
|
*
|
|
* Vollbild-Bildvorschau und Modal-Funktionen:
|
|
* - Lightbox für Arbeitsblatt-Vorschauen
|
|
* - Keyboard-Navigation (Escape zum Schließen)
|
|
* - Click-outside zum Schließen
|
|
*
|
|
* Refactored: 2026-01-19
|
|
*/
|
|
|
|
// DOM-Referenzen
|
|
let lightboxEl = null;
|
|
let lightboxImg = null;
|
|
let lightboxCaption = null;
|
|
let lightboxClose = null;
|
|
|
|
// Callback für Close-Event
|
|
let onCloseCallback = null;
|
|
|
|
/**
|
|
* Initialisiert die Lightbox
|
|
* Sucht nach Standard-IDs oder erstellt das DOM
|
|
*/
|
|
export function initLightbox() {
|
|
lightboxEl = document.getElementById('lightbox');
|
|
lightboxImg = document.getElementById('lightbox-img');
|
|
lightboxCaption = document.getElementById('lightbox-caption');
|
|
lightboxClose = document.getElementById('lightbox-close');
|
|
|
|
// Falls keine Lightbox im DOM, erstelle sie
|
|
if (!lightboxEl) {
|
|
createLightboxDOM();
|
|
}
|
|
|
|
// Event-Listener
|
|
if (lightboxClose) {
|
|
lightboxClose.addEventListener('click', closeLightbox);
|
|
}
|
|
|
|
if (lightboxEl) {
|
|
lightboxEl.addEventListener('click', (ev) => {
|
|
// Schließen bei Klick auf Hintergrund
|
|
if (ev.target === lightboxEl) {
|
|
closeLightbox();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Escape-Taste
|
|
document.addEventListener('keydown', (ev) => {
|
|
if (ev.key === 'Escape' && isLightboxOpen()) {
|
|
closeLightbox();
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Erstellt das Lightbox-DOM dynamisch
|
|
*/
|
|
function createLightboxDOM() {
|
|
lightboxEl = document.createElement('div');
|
|
lightboxEl.id = 'lightbox';
|
|
lightboxEl.className = 'lightbox hidden';
|
|
lightboxEl.innerHTML = `
|
|
<div class="lightbox-content">
|
|
<button class="lightbox-close" id="lightbox-close">Schließen ✕</button>
|
|
<img id="lightbox-img" class="lightbox-img" src="" alt="Vorschau">
|
|
<div id="lightbox-caption" class="lightbox-caption"></div>
|
|
</div>
|
|
`;
|
|
|
|
document.body.appendChild(lightboxEl);
|
|
|
|
// Referenzen aktualisieren
|
|
lightboxImg = document.getElementById('lightbox-img');
|
|
lightboxCaption = document.getElementById('lightbox-caption');
|
|
lightboxClose = document.getElementById('lightbox-close');
|
|
|
|
// CSS injizieren falls nicht vorhanden
|
|
if (!document.getElementById('lightbox-styles')) {
|
|
const style = document.createElement('style');
|
|
style.id = 'lightbox-styles';
|
|
style.textContent = `
|
|
.lightbox {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.9);
|
|
z-index: 10000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
opacity: 1;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
.lightbox.hidden {
|
|
display: none;
|
|
opacity: 0;
|
|
}
|
|
.lightbox-content {
|
|
position: relative;
|
|
max-width: 90%;
|
|
max-height: 90%;
|
|
}
|
|
.lightbox-img {
|
|
max-width: 100%;
|
|
max-height: 85vh;
|
|
object-fit: contain;
|
|
border-radius: 8px;
|
|
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
|
|
}
|
|
.lightbox-close {
|
|
position: absolute;
|
|
top: -40px;
|
|
right: 0;
|
|
background: transparent;
|
|
border: none;
|
|
color: white;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
transition: background 0.2s;
|
|
}
|
|
.lightbox-close:hover {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
}
|
|
.lightbox-caption {
|
|
color: white;
|
|
text-align: center;
|
|
margin-top: 12px;
|
|
font-size: 14px;
|
|
}
|
|
`;
|
|
document.head.appendChild(style);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Öffnet die Lightbox mit einem Bild
|
|
* @param {string} src - Bild-URL
|
|
* @param {string} [caption] - Optionale Bildunterschrift
|
|
*/
|
|
export function openLightbox(src, caption = '') {
|
|
if (!src) {
|
|
console.warn('openLightbox: No image source provided');
|
|
return;
|
|
}
|
|
|
|
// Lazy-Init falls noch nicht initialisiert
|
|
if (!lightboxEl) {
|
|
initLightbox();
|
|
}
|
|
|
|
if (lightboxImg) {
|
|
lightboxImg.src = src;
|
|
lightboxImg.alt = caption || 'Vorschau';
|
|
}
|
|
|
|
if (lightboxCaption) {
|
|
lightboxCaption.textContent = caption;
|
|
}
|
|
|
|
if (lightboxEl) {
|
|
lightboxEl.classList.remove('hidden');
|
|
// Body-Scroll verhindern
|
|
document.body.style.overflow = 'hidden';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Schließt die Lightbox
|
|
*/
|
|
export function closeLightbox() {
|
|
if (lightboxEl) {
|
|
lightboxEl.classList.add('hidden');
|
|
// Body-Scroll wiederherstellen
|
|
document.body.style.overflow = '';
|
|
}
|
|
|
|
if (lightboxImg) {
|
|
lightboxImg.src = '';
|
|
}
|
|
|
|
// Callback ausführen
|
|
if (onCloseCallback) {
|
|
onCloseCallback();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Prüft ob die Lightbox geöffnet ist
|
|
* @returns {boolean}
|
|
*/
|
|
export function isLightboxOpen() {
|
|
return lightboxEl && !lightboxEl.classList.contains('hidden');
|
|
}
|
|
|
|
/**
|
|
* Setzt einen Callback für das Close-Event
|
|
* @param {function} callback
|
|
*/
|
|
export function onClose(callback) {
|
|
onCloseCallback = callback;
|
|
}
|
|
|
|
/**
|
|
* Wechselt das Bild in der offenen Lightbox
|
|
* @param {string} src - Neue Bild-URL
|
|
* @param {string} [caption] - Neue Bildunterschrift
|
|
*/
|
|
export function changeLightboxImage(src, caption = '') {
|
|
if (lightboxImg) {
|
|
lightboxImg.src = src;
|
|
lightboxImg.alt = caption || 'Vorschau';
|
|
}
|
|
|
|
if (lightboxCaption) {
|
|
lightboxCaption.textContent = caption;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Setzt den Text des Close-Buttons
|
|
* @param {string} text - Der neue Text
|
|
*/
|
|
export function setCloseButtonText(text) {
|
|
if (lightboxClose) {
|
|
lightboxClose.textContent = text;
|
|
}
|
|
}
|