window.addEventListener('DOMContentLoaded', async () => { // Elemente holen const folderList = document.getElementById('folderList'); const addBtn = document.getElementById('addFolderBtn'); const titleEl = document.getElementById('currentTitle'); const treeviewEl = document.getElementById('folderHierarchyDropdown'); const titleArrow = document.getElementById('folderTitleArrow'); const contentList = document.getElementById('contentList'); const panel = document.querySelector('.flex-1.p-4.overflow-y-auto'); const PAGE_SIZE = 50; const paginationEl = document.createElement('div'); paginationEl.className = 'pagination flex justify-center items-center py-4 space-x-2'; contentList.parentElement.insertBefore(paginationEl, contentList); // nur einmal beim Initialisieren // Speichere zuletzt angezeigten Folder/Seite let lastFolderPath = null; let lastPage = null; const slot = document.getElementById('catSlot'); window.cat = new window.AnimeCat(slot, { images: { default: 'assets/cat/default.png', eyesClosed: 'assets/cat/eyes_closed.png', blink: 'assets/cat/blink.png', mouthOpen: 'assets/cat/mouth_open.png', joy: 'assets/cat/joy.png', mischievous: 'assets/cat/mischievous.png' } }); // Drag and Drop document.body.addEventListener('dragover', e => { e.preventDefault(); e.dataTransfer.dropEffect = 'copy'; }); document.body.addEventListener('drop', async e => { e.preventDefault(); const files = [...e.dataTransfer.files]; if (!files.length) return; // Prüfe, ob Ordner: for (let f of files) { // f ist File, hat .path und .type, aber bei Folders oft type="" (leerer String) if (f.type === "" /* = Ordner (bei DnD) */) { await window.electronAPI.addFolderByPath(f.path); await renderSidebar(); const sel = await window.electronAPI.getSelected(); if (sel) await renderContent(sel); } } }); // Farben für Sky-Mode const DAY_COLOR = [173, 216, 230]; const NIGHT_COLOR = [0, 0, 50]; function lerpColor(c1, c2, t) { return c1.map((v, i) => Math.round(v + t * (c2[i] - v))); } 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; } function updateBackground() { const factor = getTimeFactor(); const [r,g,b] = lerpColor(NIGHT_COLOR, DAY_COLOR, factor); panel.style.backgroundColor = `rgb(${r}, ${g}, ${b})`; } let skyIntervalId, titleIntervalId; function applySkyMode(enabled) { document.body.classList.toggle('sky-mode', enabled); clearInterval(skyIntervalId); clearInterval(titleIntervalId); if (enabled) { updateBackground(); skyIntervalId = setInterval(updateBackground, 60_000); function updateAllTextColors() { setTextColor('sky'); } updateAllTextColors(); titleIntervalId = setInterval(updateAllTextColors, 60_000); // damit sich die Farbe nachts/tags ändert } else { panel.style.backgroundColor = ''; setTextColor('default'); } } const initialSky = await window.settingsAPI.getSkyMode(); applySkyMode(initialSky); window.addEventListener('skymode-changed', e => applySkyMode(e.detail)); function setTextColor(mode) { // Default: Schwarz let textColor = '#111'; if (mode === 'sky') { const hour = new Date().getHours(); // Sky-Mode: Tagsüber schwarz, nachts weiß textColor = (hour >= 18 || hour < 6) ? '#fff' : '#111'; } titleEl.style.color = textColor; treeviewEl.style.color = textColor; titleArrow.style.color = textColor; // Auch die Pagination einfärben: paginationEl.style.color = textColor; // Falls Treeview geöffnet und HTML gerendert: auch alle Spans darin einfärben if (treeviewEl.querySelectorAll) { treeviewEl.querySelectorAll('.tree-file, .tree-dir').forEach(el => el.style.color = textColor); } } function basename(fullPath) { return fullPath.replace(/.*[\\/]/, ''); } // Utility für FolderObj-Suche per Path async function getFolderObjByPath(path) { const folders = await window.electronAPI.getFolders(); return folders.find(f => f.path === path) || null; } async function renderSidebar() { const folders = await window.electronAPI.getFolders(); //console.log("Renderer-Folders:", folders); const selected = await window.electronAPI.getSelected(); folderList.innerHTML = ''; folders.forEach(folderObj => { const folder = folderObj.path; const isMonitoring = folderObj.monitoring; const li = document.createElement('li'); li.setAttribute('data-folder-id', encodeURIComponent(folder)); li.className = [ 'flex items-center justify-between px-3 py-2 rounded cursor-pointer', selected && folder === selected.path ? 'selected' : '', folderObj.needsRelocation ? 'needs-relocation' : '' ].join(' '); // beide Buttons schon im Template unter „right“ li.innerHTML = `
${basename(folder)}
${ folderObj.needsRelocation ? `!` : '' }
`; // jetzt Listener setzen // relocate missing folder li.addEventListener('click', async e => { if (e.target.closest('.pause-play-btn, .remove-btn')) return; // Nur wenn Folder fehlt if (folderObj.needsRelocation) { // 1. Ordner wählen const paths = await window.electronAPI.pickFolder(); const newPath = paths && paths[0]; if (!newPath) return; // 2. Prüfen ob Git-Repo const isGit = await window.electronAPI.isGitRepo(newPath); if (!isGit) { alert('Das ist kein gültiges Git-Repository.'); return; } // 3. Hash-Vergleich const lastKnownHash = folderObj.lastHeadHash; if (!lastKnownHash) { alert('Kein gespeicherter Hash – Vergleich nicht möglich.'); return; } const isMatch = await window.electronAPI.repoHasCommit(newPath, lastKnownHash); // WAKE UP MR. FREEMAN! WAKE UP AND... SMELL THE HASHES if (!isMatch) { alert('Das ist nicht das ursprüngliche Repo (Commit-Hash fehlt).'); return; } // 4. Folder umziehen await window.electronAPI.relocateFolder(folderObj.path, newPath); await renderSidebar(); // Ggf. neuen Ordner direkt selektieren: const newFolderObj = (await window.electronAPI.getFolders()) .find(f => f.path === newPath); if (newFolderObj) { await window.electronAPI.setSelected(newFolderObj); await renderContent(newFolderObj); } return; } // Sonst Standardverhalten (Folder auswählen) await window.electronAPI.setSelected(folderObj); await renderSidebar(); await renderContent(folderObj); }); const pauseBtn = li.querySelector('.pause-play-btn'); pauseBtn.addEventListener('click', async e => { e.stopPropagation(); await window.electronAPI.setMonitoring(folderObj, !isMonitoring); await renderSidebar(); }); const removeBtn = li.querySelector('.remove-btn'); removeBtn.addEventListener('click', async e => { e.stopPropagation(); // ... dein Remove-Logic hier ... }); // Kontextmenü / Click zum Select li.addEventListener('contextmenu', e => { e.preventDefault(); window.electronAPI.showFolderContextMenu(folderObj.path); }); li.addEventListener('click', async e => { if (e.target.closest('.pause-play-btn, .remove-btn')) return; await window.electronAPI.setSelected(folderObj); await renderSidebar(); await renderContent(folderObj); }); folderList.appendChild(li); li.addEventListener('contextmenu', e => { e.preventDefault(); window.electronAPI.showFolderContextMenu(folderObj.path); }); li.addEventListener('click', async e => { if (e.target.closest('.remove-btn')) return; await window.electronAPI.setSelected(folderObj); await renderSidebar(); await renderContent(folderObj); }); li.querySelector('.remove-btn').addEventListener('click', async e => { e.stopPropagation(); const count = await window.electronAPI.getCommitCount(folderObj); const hasUnstaged= await window.electronAPI.hasDiffs(folderObj); const skipPrompt = await window.settingsAPI.getSkipPrompt(); if (count === 1 && !hasUnstaged) { if (skipPrompt) { await window.electronAPI.removeGitFolder(folderObj); } 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(folderObj); } else { return; } } } await window.electronAPI.removeFolder(folderObj); await renderSidebar(); const all = await window.electronAPI.getFolders(); if (all.length === 0) { titleEl.textContent = 'No folder selected'; contentList.innerHTML = ''; } else { const idxOld = folders.findIndex(f => f.path === folderObj.path); let idxNew = Math.max(0, idxOld - 1); const pick = all[idxNew]; await window.electronAPI.setSelected(pick); await renderSidebar(); await renderContent(pick); } }); folderList.appendChild(li); }); } // Update stats // Countdown in MM:SS-Format let countdownInterval = null; function getCommitColor(commitCount) { const stops = [ { c: 0, color: [157, 157, 157] }, { c: 5, color: [255, 255, 255] }, { c: 15, color: [30, 255, 0] }, { c: 50, color: [0, 112, 221] }, { c: 100, color: [163, 53, 238] }, { c: 500, color: [255, 128, 0] } ]; function lerp(a, b, t) { return a + (b - a) * t; } function lerpColor(a, b, t) { return [ Math.round(lerp(a[0], b[0], t)), Math.round(lerp(a[1], b[1], t)), Math.round(lerp(a[2], b[2], t)) ]; } let lower = stops[0], upper = stops[stops.length-1]; for (let i=0; i= stops[i].c && commitCount < stops[i+1].c) { lower = stops[i]; upper = stops[i+1]; break; } } const range = upper.c - lower.c || 1; const t = Math.min(Math.max((commitCount - lower.c) / range, 0), 1); return lerpColor(lower.color, upper.color, t); } function formatCountdown(ms) { if (!ms || ms <= 0) return '00:00'; const s = Math.floor(ms / 1000); const m = Math.floor(s / 60).toString().padStart(2, '0'); const sec = (s % 60).toString().padStart(2, '0'); return `${m}:${sec}`; } async function updateInteractionBar(folderObj) { // Commits Today const stats = await window.electronAPI.getDailyCommitStats(); const today = new Date().toISOString().slice(0, 10); const commitsToday = stats[today] || 0; // Globale Variablen aus Settings holen (am besten beim Start speichern) const intelligentCommitThreshold = await window.electronAPI.getIntelligentCommitThreshold?.() || 20; const minutesCommitThreshold = await window.electronAPI.getMinutesCommitThreshold?.() || 5; // Lines until next Rewrite const linesChanged = folderObj?.linesChanged || 0; const linesUntilRewrite = Math.max(0, intelligentCommitThreshold - linesChanged); // Time until next Rewrite: *Initial berechnen, Live-Countdown separat* let countdown = "00:00"; let msLeft = 0; if (folderObj?.firstCandidateBirthday) { const msThreshold = minutesCommitThreshold * 60 * 1000; const endTime = new Date(folderObj.firstCandidateBirthday).getTime() + msThreshold; msLeft = Math.max(0, endTime - Date.now()); countdown = formatCountdown(msLeft); } // In die Bar schreibenx const [r, g, b] = getCommitColor(commitsToday); document.getElementById('commitsToday').textContent = commitsToday; document.getElementById('commitsToday').style.color = `rgb(${r},${g},${b})`; document.getElementById('linesUntilRewrite').textContent = linesUntilRewrite; document.getElementById('countdown').textContent = countdown; // Live-Countdown starten, aber nur wenn noch Zeit übrig startLiveCountdown(folderObj, msLeft); } async function startLiveCountdown(folderObj, msLeft) { if (countdownInterval) clearInterval(countdownInterval); // Wenn kein Countdown nötig: 00:00 anzeigen if (!folderObj?.firstCandidateBirthday || msLeft <= 0) { document.getElementById('countdown').textContent = "00:00"; return; } const minutesCommitThreshold = await window.electronAPI.getMinutesCommitThreshold?.() || 5; const msThreshold = minutesCommitThreshold * 60 * 1000; const endTime = new Date(folderObj.firstCandidateBirthday).getTime() + msThreshold; countdownInterval = setInterval(() => { const msLeft = Math.max(0, endTime - Date.now()); document.getElementById('countdown').textContent = formatCountdown(msLeft); if (msLeft <= 0) { clearInterval(countdownInterval); } }, 1000); } const folderTitleDrop = document.getElementById('folderTitleDrop'); const folderTitleArrow = document.getElementById('folderTitleArrow'); const folderHierarchyDropdown = document.getElementById('folderHierarchyDropdown'); let isDropdownOpen = false; folderTitleDrop.addEventListener('click', async () => { if (isDropdownOpen) { closeDropdown(); return; } const selected = await window.electronAPI.getSelected(); if (!selected || !selected.path) return; // Show loading text folderHierarchyDropdown.textContent = 'Lade Verzeichnis…'; folderHierarchyDropdown.classList.remove('hidden'); folderTitleArrow.classList.add('open'); isDropdownOpen = true; // Lade Tree const tree = await window.electronAPI.getFolderTree(selected.path); console.log(selected.path); console.log(tree); folderHierarchyDropdown.innerHTML = renderFolderTreeAscii(tree, '.', ''); setTextColor(document.body.classList.contains('sky-mode') ? 'sky' : 'default'); }); folderHierarchyDropdown.addEventListener('contextmenu', function(e) { const el = e.target.closest('.tree-file, .tree-dir'); if (!el) return; e.preventDefault(); const relPath = el.getAttribute('data-path'); const type = el.getAttribute('data-type'); // "file" oder "dir" const selected = window.electronAPI.getSelectedSync?.() || (window.currentSelectedFolderObj || {}); // Tipp: Implementiere eine Sync-Methode für getSelected (siehe Preload) const absPath = selected.path + '/' + relPath; window.electronAPI.showTreeContextMenu({ absPath, relPath, root: selected.path, type }); }); function closeDropdown() { folderHierarchyDropdown.classList.add('hidden'); folderTitleArrow.classList.remove('open'); isDropdownOpen = false; } // Diese Funktion generiert die Treeview-Zeilen mit data-path und data-type function renderFolderTreeAscii(tree, prefix = '', indent = '', relPath = '.') { if (!Array.isArray(tree)) return ''; let result = ''; const lastIdx = tree.length - 1; tree.forEach((node, i) => { const isLast = i === lastIdx; const pointer = isLast ? '└── ' : '├── '; const thisRelPath = relPath === '.' ? node.name : relPath + '/' + node.name; if (node.type === 'dir') { result += `${indent}${pointer}${node.name}/\n`; const newIndent = indent + (isLast ? ' ' : '│ '); result += renderFolderTreeAscii(node.children, '', newIndent, thisRelPath); } else { result += `${indent}${pointer}${node.name}\n`; } }); return result; } window.addEventListener('repo-updated', () => { }); async function getCommitPageForHash(folderObj, hash, pageSize = PAGE_SIZE) { // Hole alle Commits (NICHT paginiert!) const { commits } = await window.electronAPI.getCommits(folderObj, 1, 100000); // genug große Zahl const idx = commits.findIndex(c => c.hash === hash || hash.startsWith(c.hash)); if (idx === -1) return 1; // fallback: erste Seite return Math.floor(idx / pageSize) + 1; } async function getPageForCurrentCommit(folderObj, pageSize = 50) { //should probably be fused with the function above // Holt alle Commits (nur die Hashes, schnell genug für moderne Maschinen) const { head, total } = await window.electronAPI.getCommits(folderObj, 1, 100000); // Großer Wert, um alle zu holen (du kannst auch einen eigenen IPC machen, der NUR die Hashes returned) const { commits } = await window.electronAPI.getCommits(folderObj, 1, total); const idx = commits.findIndex(c => c.hash === head); if (idx === -1) return 1; return Math.floor(idx / pageSize) + 1; } // Helper: gibt die Seite für einen Commit-Hash zurück async function getCommitPageForHash(folderObj, hash, pageSize) { const allHashes = await window.electronAPI.getAllCommitHashes(folderObj); // Muss in Main implementiert sein! const idx = allHashes.findIndex(h => h.startsWith(hash)); return idx === -1 ? 1 : Math.floor(idx / pageSize) + 1; } async function renderContent(folderObj, page) { closeDropdown(); const folder = folderObj.path; await updateInteractionBar(folderObj); titleEl.textContent = folder; setTextColor(document.body.classList.contains('sky-mode') ? 'sky' : 'default'); // --- Seitenwahl beim Ordnerwechsel --- let usePage = page; if (!usePage || folder !== lastFolderPath) { // Wenn keine Seite gegeben oder Ordner gewechselt: Seite für aktuellen HEAD suchen const { head } = await window.electronAPI.getCommits(folderObj, 1, 1); // nur für den Hash! usePage = await getCommitPageForHash(folderObj, head, PAGE_SIZE); } lastFolderPath = folder; lastPage = usePage; // Paginierte Commits holen const { head, commits, total, page: currentPage, pageSize, pages } = await window.electronAPI.getCommits(folderObj, usePage, PAGE_SIZE); if (!commits || !commits.length) { contentList.innerHTML = '
No commits found.
'; paginationEl.innerHTML = ''; return; } // Commit-Liste rendern contentList.innerHTML = commits.map(c => { const isQueued = folderObj.llmCandidates && folderObj.llmCandidates.some(fullHash => fullHash.startsWith(c.hash) ); return `
  • ${c.hash} ${new Date(c.date).toLocaleString()}
    ${c.message}
    
            
    ${ isQueued ? `In Rewrite Queue` : '' }
  • `; }).join(''); // --- PAGINATION --- if (pages > 1) { paginationEl.innerHTML = ` Seite ${currentPage} / ${pages} `; paginationEl.querySelector('#page-prev').onclick = () => renderContent(folderObj, currentPage - 1); paginationEl.querySelector('#page-next').onclick = () => renderContent(folderObj, currentPage + 1); paginationEl.style.display = 'flex'; } else { paginationEl.innerHTML = ''; paginationEl.style.display = 'none'; } // 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(folderObj, 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()) { const diff = await window.electronAPI.diffCommit(folderObj, 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 { const savedPath = await window.electronAPI.snapshotCommit(folderObj, hash); if (savedPath) { alert(`Snapshot gespeichert unter:\n${savedPath}`); } } catch (err) { console.error(err); alert('Snapshot fehlgeschlagen'); } }); }); // Checkout-Button: Seite neu bestimmen! contentList.querySelectorAll('.checkout-btn').forEach(btn => { btn.addEventListener('click', async () => { const hash = btn.dataset.hash; await window.electronAPI.checkoutCommit(folderObj, hash); const page = await getCommitPageForHash(folderObj, hash, PAGE_SIZE); await renderContent(folderObj, page); }); }); // Copy-Diff-Button contentList.querySelectorAll('.diff-container').forEach(container => { const btn = container.querySelector('.copy-diff-btn'); const pre = container.querySelector('pre'); const originalSVG = btn.innerHTML; const checkSVG = ` `; btn.addEventListener('click', () => { navigator.clipboard.writeText(pre.textContent) .then(() => { btn.innerHTML = checkSVG; setTimeout(() => { btn.innerHTML = originalSVG; }, 1000); }) .catch(err => { console.error('Clipboard write failed', err); }); }); }); // --- Aktuellen Commit in die Mitte scrollen (falls vorhanden) --- const currentEl = contentList.querySelector('li.current-commit'); if (currentEl) { currentEl.scrollIntoView({ behavior: 'smooth', block: 'center' }); } } await renderSidebar(); const initial = await window.electronAPI.getSelected(); if (initial) await renderContent(initial); addBtn.addEventListener('click', async () => { await window.electronAPI.addFolder(); await renderSidebar(); const sel = await window.electronAPI.getSelected(); if (sel) await renderContent(sel); }); // Repo-Update Handling jetzt mit Lookup für folderObj window.addEventListener('repo-updated', async e => { closeDropdown(); const obj = await getFolderObjByPath(e.detail); if (!obj) return; // Hole aktuell selektierten Ordner const selected = await window.electronAPI.getSelected(); if (!selected || selected.path !== obj.path) { await window.electronAPI.setSelected(obj); await renderSidebar(); } await renderContent(obj); }); titleEl.addEventListener('contextmenu', e => { e.preventDefault(); if (titleEl.textContent !== 'No folder selected') { window.electronAPI.showFolderContextMenu(titleEl.textContent); } }); /* const commitBtn = document.getElementById('commitBtn'); commitBtn.addEventListener('click', async () => { const folderObj = await window.electronAPI.getSelected(); if (!folderObj || !folderObj.path) { alert('Kein Ordner ausgewählt!'); return; } const message = 'test' commitBtn.disabled = true; commitBtn.textContent = 'Committing…'; const result = await window.electronAPI.commitCurrentFolder(folderObj, message); if (result.success) { alert('Commit erfolgreich!'); await renderContent(folderObj); } else { alert('Commit fehlgeschlagen:\n' + result.error); } commitBtn.disabled = false; commitBtn.textContent = 'Commit'; }); */ window.electronAPI.onTrayToggleMonitoring(async (_e, folderPath) => { const folders = await window.electronAPI.getFolders(); const folder = folders.find(f => f.path === folderPath); if (folder) { await window.electronAPI.setMonitoring(folder, !folder.monitoring); await renderSidebar(); // <--- UI REFRESH! const selected = await window.electronAPI.getSelected(); if (selected && selected.path === folderPath) await renderContent(folder); // falls im Content aktiv } }); window.electronAPI.onTrayRemoveFolder(async (_e, folderPath) => { const folders = await window.electronAPI.getFolders(); const folder = folders.find(f => f.path === folderPath); if (folder) { await window.electronAPI.removeFolder(folder); await renderSidebar(); // <--- UI REFRESH! // Wähle ggf. neuen Folder oder setze UI auf "No folder selected" const all = await window.electronAPI.getFolders(); if (all.length === 0) { titleEl.textContent = 'No folder selected'; contentList.innerHTML = ''; } else { await window.electronAPI.setSelected(all[0]); await renderContent(all[0]); } } }); window.electronAPI.onTrayAddFolder(async () => { await window.electronAPI.addFolder(); await renderSidebar(); // <--- UI REFRESH! const sel = await window.electronAPI.getSelected(); if (sel) await renderContent(sel); }); window.electronAPI.onFoldersLocationUpdated(folderObj => { const selector = `[data-folder-id="${encodeURIComponent(folderObj.path)}"]`; console.log('Selector:', selector); const li = document.querySelector(selector); console.log('Gefundenes li:', li); if (li) { if (folderObj.needsRelocation) { li.classList.add('needs-relocation'); li.setAttribute.disabled; // evtl. !-Icon einblenden } else { li.classList.remove('needs-relocation'); // evtl. !-Icon ausblenden } renderSidebar(); } }); /* window.cat.beginSpeech(); let msg = "Ich bin wieder da!"; for (let ch of msg) { setTimeout(() => window.cat.appendSpeech(ch), 50); } setTimeout(() => window.cat.endSpeech(), 2000); */ window.electronAPI.onCatBegin(() => window.cat && window.cat.beginSpeech()); window.electronAPI.onCatChunk((_e, chunk) => window.cat && window.cat.appendSpeech(chunk)); window.electronAPI.onCatEnd(() => window.cat && window.cat.endSpeech()); const speakToCat = msg => { window.cat.beginSpeech(); // Zeig Nachricht Zeichen für Zeichen: let i = 0; function nextChar() { if (i < msg.length) { window.cat.appendSpeech(msg[i++]); setTimeout(nextChar, 50); } else { window.cat.endSpeech(); } } nextChar(); }; /* const slot = document.getElementById('catSlot'); const cat = new AnimeCat(slot, { images: { default: 'assets/cat/default.png', eyesClosed: 'assets/cat/eyes_closed.png', mouthOpen: 'assets/cat/mouth_open.png' } }); // Stelle sie global zur Verfügung: window.cat = { begin: () => cat.beginSpeech(), streamText: txt => cat.appendSpeech(txt), end: () => cat.endSpeech() }; */ // Hole die Commit-Stats beim Laden der Seite window.electronAPI.getDailyCommitStats().then(stats => { const today = new Date().toISOString().slice(0, 10); const todayCount = stats[today] || 0; console.log('Commits heute:', todayCount); window.updateCatGlow(todayCount); }); window.updateCatGlow = function(commitCount) { if (window.cat) window.cat.animateCatGlow(commitCount); }; });