diff --git a/public/app.js b/public/app.js index 2c4c80f..019aa0d 100644 --- a/public/app.js +++ b/public/app.js @@ -83,6 +83,22 @@ function featuresForProject(features, projectId) { return features.filter((f) => !f.projectId || f.projectId === projectId) } +const PREVIEW_BASE_WIDTH = 1280 +function setupPreviewFrame(frame) { + const view = frame.querySelector('.preview-frame__view') + if (!view) return + const apply = () => { + const w = frame.clientWidth + if (w) view.style.transform = `scale(${w / PREVIEW_BASE_WIDTH})` + } + if (typeof ResizeObserver !== 'undefined') { + new ResizeObserver(apply).observe(frame) + } else { + window.addEventListener('resize', apply) + } + apply() +} + function renderProjectCard(project, features) { const li = document.createElement('li') li.className = 'project-card' @@ -91,14 +107,25 @@ function renderProjectCard(project, features) { ? `
${projectFeatures.map((f) => `${escapeHtml(f.featureKey)}`).join('')}
` : '' + const ready = isProjectPreviewReady(project) + const url = project.previewUrl ? escapeAttr(project.previewUrl) : '' + const previewHtml = ready && url + ? ` + + + Webseite öffnen ↗ + ` + : `
${project.previewUrl ? 'Vorschau wird vorbereitet …' : 'Noch keine Webseite veröffentlicht'}
` + li.innerHTML = ` + ${previewHtml}

${escapeHtml(project.projectName || project.subdomain || 'Projekt')}

Subdomain
${escapeHtml(project.subdomain || '–')}
-
Vorschau
${project.previewUrl ? `Projekt-Webseite öffnen` : '–'}
Live-Domain
${project.liveDomain ? escapeHtml(project.liveDomain) : '–'}
Status
${escapeHtml(project.status || '–')}
+ ${ready && url ? `

Projekt-Webseite öffnen ↗

` : ''} ${featureHtml}
@@ -106,6 +133,9 @@ function renderProjectCard(project, features) { ` + const frame = li.querySelector('[data-preview-frame]') + if (frame) setupPreviewFrame(frame) + const btn = li.querySelector('[data-details-btn]') const box = li.querySelector('[data-details-box]') let loaded = false diff --git a/public/dashboard.html b/public/dashboard.html index 0c38604..6a8a798 100644 --- a/public/dashboard.html +++ b/public/dashboard.html @@ -61,6 +61,31 @@ .commit-meta { font-size: 12px; opacity: 0.6; } .commit-body { font-size: 13px; opacity: 0.8; white-space: pre-wrap; margin: 2px 0 0 8px; } + /* Projekt-Vorschaufenster */ + .preview-frame { + position: relative; display: block; width: 100%; aspect-ratio: 16 / 10; + overflow: hidden; border-radius: 12px; border: 1px solid rgba(255,255,255,0.14); + background: rgba(255,255,255,0.04); margin-bottom: 1rem; text-decoration: none; + transition: border-color 0.2s ease, box-shadow 0.2s ease; + } + .preview-frame:hover { border-color: rgba(157,61,254,0.7); box-shadow: 0 0 0 1px rgba(157,61,254,0.35); } + .preview-frame__view { + position: absolute; top: 0; left: 0; width: 1280px; height: 800px; border: 0; + transform-origin: 0 0; pointer-events: none; background: #fff; + } + .preview-frame__hint { + position: absolute; left: 0; right: 0; bottom: 0; padding: 10px 14px; + font-size: 13px; font-weight: 600; color: #fff; + background: linear-gradient(to top, rgba(0,0,0,0.6), rgba(0,0,0,0)); + opacity: 0; transition: opacity 0.2s ease; pointer-events: none; + } + .preview-frame:hover .preview-frame__hint { opacity: 1; } + .preview-frame--empty { + display: flex; align-items: center; justify-content: center; + color: rgba(255,255,255,0.5); font-size: 14px; aspect-ratio: 16 / 10; + } + .preview-open { margin: 0.75rem 0 0; } + /* Chat */ .chat-box { display: flex; flex-direction: column; height: 480px; } .chat-messages { flex: 1; overflow-y: auto; padding: 6px 4px; display: flex; flex-direction: column; gap: 10px; }