// renderer.js window.addEventListener('DOMContentLoaded', async () => { // Elemente holen const folderList = document.getElementById('folderList'); const addBtn = document.getElementById('addFolderBtn'); const titleEl = document.getElementById('currentTitle'); const contentList = document.getElementById('contentList'); const panel = document.querySelector('.flex-1.p-4.overflow-y-auto'); // 1) Baby-Blau und Nacht-Blau als RGB-Arrays const DAY_COLOR = [173, 216, 230]; const NIGHT_COLOR = [0, 0, 50]; // 2) Linearer Interpolator function lerpColor(c1, c2, t) { return c1.map((v, i) => Math.round(v + t * (c2[i] - v))); } // 3) Entscheider für den Zeit-Factor function getTimeFactor() { const now = new Date(); const md = now.getHours() * 60 + now.getMinutes(); if (md < 4 * 60) return 0; if (md < 8 * 60) return (md - 4*60) / (4*60); if (md < 16 * 60) return 1; if (md < 20 * 60) return 1 - ((md - 16*60) / (4*60)); return 0; } // 4) setzt die Hintergrundfarbe function updateBackground() { const factor = getTimeFactor(); const [r,g,b] = lerpColor(NIGHT_COLOR, DAY_COLOR, factor); panel.style.backgroundColor = `rgb(${r}, ${g}, ${b})`; } // 5) applySkyMode-Funktion let skyIntervalId, titleIntervalId; function applySkyMode(enabled) { document.body.classList.toggle('sky-mode', enabled); // alte Intervalle löschen clearInterval(skyIntervalId); clearInterval(titleIntervalId); if (enabled) { // Hintergrund updaten updateBackground(); skyIntervalId = setInterval(updateBackground, 60_000); // Titel-Farbe je nach Uhrzeit function updateTitleColor() { const hour = new Date().getHours(); if (hour >= 18 || hour < 6) { titleEl.style.color = '#fff'; } else { titleEl.style.color = ''; } } updateTitleColor(); titleIntervalId = setInterval(updateTitleColor, 60_000); } else { // Sky-Mode aus → zurücksetzen panel.style.backgroundColor = ''; titleEl.style.color = ''; } } // 6) initial anwenden und auf Event lauschen const initialSky = await window.settingsAPI.getSkyMode(); applySkyMode(initialSky); window.addEventListener('skymode-changed', e => applySkyMode(e.detail)); // … restliches Rendering wie gehabt … function basename(fullPath) { return fullPath.replace(/.*[\\/]/, ''); } async function renderSidebar() { const folders = await window.electronAPI.getFolders(); const selected = await window.electronAPI.getSelected(); folderList.innerHTML = ''; folders.forEach(folder => { // 1) li anlegen, selected-Klasse statt bg-[#fecdd3] const li = document.createElement('li'); li.className = [ 'flex items-center justify-between px-3 py-2 rounded cursor-pointer', folder === selected ? 'selected' : '' ].join(' '); // 2) inneres Markup, ohne Hard-Coded-Farben li.innerHTML = `
${basename(folder)}
`; // rechter Mausklick in der Sidebar li.addEventListener('contextmenu', e => { e.preventDefault(); window.electronAPI.showFolderContextMenu(folder); }); // 3) Klick auf li (Select) li.addEventListener('click', async e => { if (e.target.closest('.remove-btn')) return; await window.electronAPI.setSelected(folder); await renderSidebar(); await renderContent(folder); }); // 4) Remove-Button li.querySelector('.remove-btn').addEventListener('click', async e => { e.stopPropagation(); // Commit-Count / Diffs / SkipPrompt-Logik wie gehabt const count = await window.electronAPI.getCommitCount(folder); const hasUnstaged= await window.electronAPI.hasDiffs(folder); const skipPrompt = await window.settingsAPI.getSkipPrompt(); if (count === 1 && !hasUnstaged) { if (skipPrompt) { await window.electronAPI.removeGitFolder(folder); } else { const ok = confirm( 'Dieser Ordner hat nur einen Initial-Commit und keine Änderungen.\n' + 'Möchtest du das gesamte Git-Repository (den .git-Ordner) löschen?' ); if (ok) { await window.electronAPI.removeGitFolder(folder); } else { return; } } } // 5) aus Sidebar/Store entfernen await window.electronAPI.removeFolder(folder); await renderSidebar(); // 6) neue Selektion const all = await window.electronAPI.getFolders(); if (all.length === 0) { titleEl.textContent = 'No folder selected'; contentList.innerHTML = ''; } else { // entfernten Index vorher merken, dann neuen auswählen const idxOld = folders.indexOf(folder); let idxNew = Math.max(0, idxOld - 1); const pick = all[idxNew]; await window.electronAPI.setSelected(pick); await renderSidebar(); await renderContent(pick); } }); folderList.appendChild(li); }); } async function renderContent(folder) { const currentFolder = folder; titleEl.textContent = folder; const { head, commits } = await window.electronAPI.getCommits(folder); contentList.innerHTML = commits.map(c => `
  • ${c.hash} ${new Date(c.date).toLocaleString()}
    ${c.message}
    
            
  • `).join(''); // Erst mal alle Diff-Buttons prüfen und ggf. deaktivieren contentList.querySelectorAll('.diff-btn').forEach(async btn => { const hash = btn.dataset.hash; const diffText = await window.electronAPI.diffCommit(folder, hash); if (!diffText.trim()) { btn.disabled = true; btn.classList.add('disabled'); } }); // Diff-Toggle & Highlighting contentList.querySelectorAll('.diff-btn').forEach(btn => { btn.addEventListener('click', async () => { const li = btn.closest('li'); const hash = btn.dataset.hash; const svg = btn.querySelector('svg'); const container = li.querySelector('.diff-container'); const pre = container.querySelector('pre'); if (!pre.innerHTML.trim()) { // fetch und HTML-Snippet bauen const diff = await window.electronAPI.diffCommit(folder, hash); const escaped = diff .replace(/&/g, '&') .replace(//g, '>'); pre.innerHTML = escaped .split('\n') .map(line => { const cls = line.startsWith('+') ? 'diff-line addition' : line.startsWith('-') ? 'diff-line deletion' : 'diff-line'; return `
    ${line}
    `; }) .join(''); } const isOpen = container.classList.toggle('open'); if (isOpen) { container.style.maxHeight = container.scrollHeight + 'px'; } else { container.style.maxHeight = '0'; } svg.classList.toggle('open', isOpen); }); }); // Snapshot-Button contentList.querySelectorAll('.snapshot-btn').forEach(btn => { btn.addEventListener('click', async () => { const hash = btn.dataset.hash; try { // verwende jetzt currentFolder statt einer undefinierten Variable const savedPath = await window.electronAPI.snapshotCommit(currentFolder, hash); if (savedPath) { alert(`Snapshot gespeichert unter:\n${savedPath}`); } } catch (err) { console.error(err); alert('Snapshot fehlgeschlagen'); } }); }); // Revert-Button contentList.querySelectorAll('.revert-btn').forEach(btn => { btn.addEventListener('click', async () => { const hash = btn.dataset.hash; if (confirm(`Commit ${hash} wirklich revertieren?`)) { await window.electronAPI.revertCommit(folder, hash); await renderContent(folder); } }); }); // Checkout-Button contentList.querySelectorAll('.checkout-btn').forEach(btn => { btn.addEventListener('click', async () => { const hash = btn.dataset.hash; //if (!confirm(`Jump here? Ungestagte Änderungen werden verworfen.`)) return; // currentFolder hast du oben in renderContent gespeichert await window.electronAPI.checkoutCommit(currentFolder, hash); await renderContent(currentFolder); }); }); const currentEl = contentList.querySelector('li.current-commit'); // Copy-Diff-Button contentList.querySelectorAll('.diff-container').forEach(container => { const btn = container.querySelector('.copy-diff-btn'); const pre = container.querySelector('pre'); // speicher die ursprüngliche SVG const originalSVG = btn.innerHTML; // erstelle die Checkmark-SVG const checkSVG = ` `; btn.addEventListener('click', () => { navigator.clipboard.writeText(pre.textContent) .then(() => { // Icon tauschen btn.innerHTML = checkSVG; // nach 1s wieder zurück setTimeout(() => { btn.innerHTML = originalSVG; }, 1000); }) .catch(err => { console.error('Clipboard write failed', err); }); }); }); if (currentEl) { // weich scrollen und zentriert in den Viewport bringen currentEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } await renderSidebar(); const initial = await window.electronAPI.getSelected(); if (initial) await renderContent(initial); // Add-Folder addBtn.addEventListener('click', async () => { await window.electronAPI.addFolder(); await renderSidebar(); const sel = await window.electronAPI.getSelected(); if (sel) await renderContent(sel); }); // repo-updated, contextmenus usw. window.addEventListener('repo-updated', e => { if (titleEl.textContent === e.detail) renderContent(e.detail); }); titleEl.addEventListener('contextmenu', e => { e.preventDefault(); if (titleEl.textContent !== 'No folder selected') { window.electronAPI.showFolderContextMenu(titleEl.textContent); } }); });