Help me with my renderer.js code it hates meeee </3
window.addEventListener('DOMContentLoaded', () => {
console.log("✅ DOM loaded, checking preload bridge");
// 🌟 Element references
const fpsDisplay = document.getElementById('fps-display');
const ctx = document.getElementById('fpsChart').getContext('2d');
const previewVideo = document.getElementById('preview');
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const exportBtn = document.getElementById('exportBtn');
const recordBtn = document.getElementById('recordBtn');
const recordStatus = document.getElementById('recordStatus');
const sourcePicker = document.getElementById('sourcePicker');
const sessionTimer = document.getElementById('sessionTimer');
const historyBtn = document.getElementById('historyBtn');
const historyPanel = document.getElementById('historyPanel');
// 📜 Session history
const history = JSON.parse(localStorage.getItem('recordingHistory')) || [];
function addSessionEntry(entry) {
history.push(entry);
localStorage.setItem('recordingHistory', JSON.stringify(history));
}
historyBtn.onclick = () => {
const storedHistory = JSON.parse(localStorage.getItem('recordingHistory')) || [];
historyPanel.innerHTML = storedHistory.map(entry => `
<div class="history-entry">
<strong>${entry.windowName}</strong> — ${entry.duration}<br>
📈 Max FPS: ${entry.maxFPS} 📉 Min FPS: ${entry.minFPS}<br>
📤 Exported: ${entry.exportTime}
</div>
`).join('');
historyPanel.classList.toggle('hidden');
};
// 🔧 Buffers & state
const fpsLog = [];
const timestamps = [];
const pointColors = [];
let intervalID = null;
let monitorInterval = null;
let isRecording = false;
let mediaRecorder = null;
let recordedChunks = [];
let timerInterval = null;
let startTime = null;
// 📈 Setup chart
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: timestamps,
datasets: [{
label: 'FPS',
data: fpsLog,
pointBackgroundColor: pointColors,
borderColor: 'rgba(100,100,100,0.5)',
backgroundColor: 'rgba(150,200,255,0.2)',
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
x: { title: { display: true, text: 'Time' } },
y: { title: { display: true, text: 'Frames per Second' }, min: 0, max: 100 }
}
}
});
window.addEventListener('resize', () => chart.resize());
// 🔁 Preload bridge retry
function tryListSources(attempt = 1) {
if (!window.electronAPI?.getSources) {
console.warn(`⚠ Bridge not ready (attempt ${attempt})`);
setTimeout(() => tryListSources(attempt + 1), 500);
return;
}
listSources();
}
tryListSources();
// 🖥 List sources
async function listSources() {
sourcePicker.classList.add('loading');
sourcePicker.innerHTML = '<option>Loading…</option>';
try {
const sources = await window.electronAPI.getSources();
sourcePicker.classList.remove('loading');
sourcePicker.innerHTML = "";
if (!sources.length) {
sourcePicker.innerHTML = "<option>No windows found</option>";
return;
}
for (const source of sources) {
const opt = document.createElement('option');
opt.value = source.id;
opt.textContent = source.name;
sourcePicker.appendChild(opt);
}
sourcePicker.onchange = () => {
const sourceId = sourcePicker.value;
const sourceName = sourcePicker.selectedOptions[0]?.textContent || "Unknown";
recordStatus.textContent = `🔍 Previewing: ${sourceName}`;
startStream(sourceId);
};
sourcePicker.value = sources[0].id;
recordStatus.textContent = `🔍 Previewing: ${sources[0].name}`;
startStream(sources[0].id);
} catch (err) {
console.error("❌ Source error:", err);
sourcePicker.classList.remove('loading');
sourcePicker.innerHTML = "<option>Failed to load sources</option>";
}
}
// 🎥 Start stream
async function startStream(sourceId) {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: sourceId
}
}
});
previewVideo.srcObject = stream;
recordStatus.textContent = "✅ Live preview started";
} catch (err) {
console.error("Stream error:", err);
previewVideo.srcObject = null;
recordStatus.textContent = "❌ Could not start preview";
}
}
// 📈 FPS tracker
function recordFPS() {
const fps = Math.floor(Math.random() * 100);
const time = new Date().toLocaleTimeString();
const color = fps < 30 ? 'rgba(255,0,0,0.8)' : 'rgba(0,0,255,0.8)';
fpsDisplay.textContent = `FPS: ${fps}`;
fpsDisplay.classList.remove('fps-low', 'fps-mid', 'fps-high');
fpsDisplay.classList.add(fps < 30 ? 'fps-low' : fps < 60 ? 'fps-mid' : 'fps-high');
fpsLog.push(fps);
timestamps.push(time);
pointColors.push(color);
if (fpsLog.length > 30) {
fpsLog.shift();
timestamps.shift();
pointColors.shift();
}
chart.update();
}
// 📉 FPS monitor
function monitorFPS() {
monitorInterval = setInterval(() => {
const latest = fpsLog.at(-1);
if (latest > 30 && isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
recordBtn.classList.remove('recording');
sessionTimer.textContent = "";
recordStatus.textContent = "";
isRecording = false;
}
}, 1000);
}
// ▶ Start logging
startBtn.onclick = () => {
if (!intervalID) {
intervalID = setInterval(recordFPS, 1000);
console.log("▶ Started FPS logging");
}
};
// ⏹ Stop logging
stopBtn.onclick = () => {
clearInterval(intervalID);
intervalID = null;
console.log("⏹ Stopped FPS logging");
};
// 📤 Export CSV
exportBtn.onclick = () => {
const csv = fpsLog.map((fps, i) => `${timestamps[i]},${fps}`).join('\n');
const blob = new Blob([`Timestamp,FPS\n${csv}`], { type: 'text/csv' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'fps_log.csv';
link.click();
};
// ⏺ Record toggle
recordBtn.onclick = () => {
if (isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
clearInterval(intervalID);
intervalID = null;
sessionTimer.textContent = "";
recordBtn.classList.remove('recording');
recordStatus.textContent = "";
isRecording = false;
return;
}
if (!previewVideo.srcObject) return;
recordedChunks = [];
mediaRecorder = new MediaRecorder(previewVideo.srcObject, {
mimeType: 'video/webm; codecs=vp9'
});
mediaRecorder.ondataavailable = e => {
if (e.data.size) recordedChunks.push(e.data);
};
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'low_fps_capture.webm';
link.click();
addSessionEntry({
duration: sessionTimer.textContent || "Unknown",
minFPS: Math.min(...fpsLog),
maxFPS: Math.max(...fpsLog),
exportTime: new Date().toLocaleString(),
windowName: sourcePicker.selectedOptions[0]?.textContent || "Unknown"
});
clearInterval(intervalID); // stop FPS logging
intervalID = null;
};
mediaRecorder.start();
isRecording = true;
recordBtn.classList.add('recording');
recordStatus.textContent = "Recording…";
monitorFPS();
if (!intervalID) {
intervalID = setInterval(recordFPS, 1000);
console.log("▶ Started FPS logging (via Record)");
}
startTime = Date.now();
timerInterval = setInterval(() => {
const secs = Math.floor((Date.now() - startTime) / 1000);
const mins = String(Math.floor(secs / 60)).padStart(2, '0');
const sec = String(secs % 60).padStart(2, '0');
sessionTimer.textContent = `⏱ ${mins}:${sec}`;
}, 1000);
setTimeout(() => {
if (isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
clearInterval(intervalID);
intervalID = null;
sessionTimer.textContent = "";
recordBtn.classList.remove('recording');
recordStatus.textContent = "";
isRecording = false;
console.log("⏹ Auto-stopped after max duration");
}
}, 180000);
};
console.log("✅ DOM loaded, checking preload bridge");
// 🌟 Element references
const fpsDisplay = document.getElementById('fps-display');
const ctx = document.getElementById('fpsChart').getContext('2d');
const previewVideo = document.getElementById('preview');
const startBtn = document.getElementById('startBtn');
const stopBtn = document.getElementById('stopBtn');
const exportBtn = document.getElementById('exportBtn');
const recordBtn = document.getElementById('recordBtn');
const recordStatus = document.getElementById('recordStatus');
const sourcePicker = document.getElementById('sourcePicker');
const sessionTimer = document.getElementById('sessionTimer');
const historyBtn = document.getElementById('historyBtn');
const historyPanel = document.getElementById('historyPanel');
// 📜 Session history
const history = JSON.parse(localStorage.getItem('recordingHistory')) || [];
function addSessionEntry(entry) {
history.push(entry);
localStorage.setItem('recordingHistory', JSON.stringify(history));
}
historyBtn.onclick = () => {
const storedHistory = JSON.parse(localStorage.getItem('recordingHistory')) || [];
historyPanel.innerHTML = storedHistory.map(entry => `
<div class="history-entry">
<strong>${entry.windowName}</strong> — ${entry.duration}<br>
📈 Max FPS: ${entry.maxFPS} 📉 Min FPS: ${entry.minFPS}<br>
📤 Exported: ${entry.exportTime}
</div>
`).join('');
historyPanel.classList.toggle('hidden');
};
// 🔧 Buffers & state
const fpsLog = [];
const timestamps = [];
const pointColors = [];
let intervalID = null;
let monitorInterval = null;
let isRecording = false;
let mediaRecorder = null;
let recordedChunks = [];
let timerInterval = null;
let startTime = null;
// 📈 Setup chart
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: timestamps,
datasets: [{
label: 'FPS',
data: fpsLog,
pointBackgroundColor: pointColors,
borderColor: 'rgba(100,100,100,0.5)',
backgroundColor: 'rgba(150,200,255,0.2)',
fill: true,
tension: 0.3
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
scales: {
x: { title: { display: true, text: 'Time' } },
y: { title: { display: true, text: 'Frames per Second' }, min: 0, max: 100 }
}
}
});
window.addEventListener('resize', () => chart.resize());
// 🔁 Preload bridge retry
function tryListSources(attempt = 1) {
if (!window.electronAPI?.getSources) {
console.warn(`⚠ Bridge not ready (attempt ${attempt})`);
setTimeout(() => tryListSources(attempt + 1), 500);
return;
}
listSources();
}
tryListSources();
// 🖥 List sources
async function listSources() {
sourcePicker.classList.add('loading');
sourcePicker.innerHTML = '<option>Loading…</option>';
try {
const sources = await window.electronAPI.getSources();
sourcePicker.classList.remove('loading');
sourcePicker.innerHTML = "";
if (!sources.length) {
sourcePicker.innerHTML = "<option>No windows found</option>";
return;
}
for (const source of sources) {
const opt = document.createElement('option');
opt.value = source.id;
opt.textContent = source.name;
sourcePicker.appendChild(opt);
}
sourcePicker.onchange = () => {
const sourceId = sourcePicker.value;
const sourceName = sourcePicker.selectedOptions[0]?.textContent || "Unknown";
recordStatus.textContent = `🔍 Previewing: ${sourceName}`;
startStream(sourceId);
};
sourcePicker.value = sources[0].id;
recordStatus.textContent = `🔍 Previewing: ${sources[0].name}`;
startStream(sources[0].id);
} catch (err) {
console.error("❌ Source error:", err);
sourcePicker.classList.remove('loading');
sourcePicker.innerHTML = "<option>Failed to load sources</option>";
}
}
// 🎥 Start stream
async function startStream(sourceId) {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: sourceId
}
}
});
previewVideo.srcObject = stream;
recordStatus.textContent = "✅ Live preview started";
} catch (err) {
console.error("Stream error:", err);
previewVideo.srcObject = null;
recordStatus.textContent = "❌ Could not start preview";
}
}
// 📈 FPS tracker
function recordFPS() {
const fps = Math.floor(Math.random() * 100);
const time = new Date().toLocaleTimeString();
const color = fps < 30 ? 'rgba(255,0,0,0.8)' : 'rgba(0,0,255,0.8)';
fpsDisplay.textContent = `FPS: ${fps}`;
fpsDisplay.classList.remove('fps-low', 'fps-mid', 'fps-high');
fpsDisplay.classList.add(fps < 30 ? 'fps-low' : fps < 60 ? 'fps-mid' : 'fps-high');
fpsLog.push(fps);
timestamps.push(time);
pointColors.push(color);
if (fpsLog.length > 30) {
fpsLog.shift();
timestamps.shift();
pointColors.shift();
}
chart.update();
}
// 📉 FPS monitor
function monitorFPS() {
monitorInterval = setInterval(() => {
const latest = fpsLog.at(-1);
if (latest > 30 && isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
recordBtn.classList.remove('recording');
sessionTimer.textContent = "";
recordStatus.textContent = "";
isRecording = false;
}
}, 1000);
}
// ▶ Start logging
startBtn.onclick = () => {
if (!intervalID) {
intervalID = setInterval(recordFPS, 1000);
console.log("▶ Started FPS logging");
}
};
// ⏹ Stop logging
stopBtn.onclick = () => {
clearInterval(intervalID);
intervalID = null;
console.log("⏹ Stopped FPS logging");
};
// 📤 Export CSV
exportBtn.onclick = () => {
const csv = fpsLog.map((fps, i) => `${timestamps[i]},${fps}`).join('\n');
const blob = new Blob([`Timestamp,FPS\n${csv}`], { type: 'text/csv' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'fps_log.csv';
link.click();
};
// ⏺ Record toggle
recordBtn.onclick = () => {
if (isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
clearInterval(intervalID);
intervalID = null;
sessionTimer.textContent = "";
recordBtn.classList.remove('recording');
recordStatus.textContent = "";
isRecording = false;
return;
}
if (!previewVideo.srcObject) return;
recordedChunks = [];
mediaRecorder = new MediaRecorder(previewVideo.srcObject, {
mimeType: 'video/webm; codecs=vp9'
});
mediaRecorder.ondataavailable = e => {
if (e.data.size) recordedChunks.push(e.data);
};
mediaRecorder.onstop = () => {
const blob = new Blob(recordedChunks, { type: 'video/webm' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'low_fps_capture.webm';
link.click();
addSessionEntry({
duration: sessionTimer.textContent || "Unknown",
minFPS: Math.min(...fpsLog),
maxFPS: Math.max(...fpsLog),
exportTime: new Date().toLocaleString(),
windowName: sourcePicker.selectedOptions[0]?.textContent || "Unknown"
});
clearInterval(intervalID); // stop FPS logging
intervalID = null;
};
mediaRecorder.start();
isRecording = true;
recordBtn.classList.add('recording');
recordStatus.textContent = "Recording…";
monitorFPS();
if (!intervalID) {
intervalID = setInterval(recordFPS, 1000);
console.log("▶ Started FPS logging (via Record)");
}
startTime = Date.now();
timerInterval = setInterval(() => {
const secs = Math.floor((Date.now() - startTime) / 1000);
const mins = String(Math.floor(secs / 60)).padStart(2, '0');
const sec = String(secs % 60).padStart(2, '0');
sessionTimer.textContent = `⏱ ${mins}:${sec}`;
}, 1000);
setTimeout(() => {
if (isRecording) {
mediaRecorder.stop();
clearInterval(monitorInterval);
clearInterval(timerInterval);
clearInterval(intervalID);
intervalID = null;
sessionTimer.textContent = "";
recordBtn.classList.remove('recording');
recordStatus.textContent = "";
isRecording = false;
console.log("⏹ Auto-stopped after max duration");
}
}, 180000);
};