// components/carbon-tracker.js - Track carbon emissions per model import { StateManager } from '../services/state-manager.js'; import { TranslationService } from '../services/translation-service.js'; export const CarbonTracker = { elements: { toolbarEmissions: null, moreInfoBtn: null, emissionsModal: null, okModalBtn: null, closeModalBtn: null, emissionsTableBody: null, totalEmissionsCell: null, totalTokensCell: null, totalRepliesCell: null, }, // Model display names modelNames: { "champ": "CHAMP_v1", "qwen": "CHAMP_v2", "openai": "GPT-5.2", "google-conservative": translations[StateManager.currentLang]["gemini_conservative"], "google-creative": translations[StateManager.currentLang]["gemini_creative"], }, /** * Initialize the carbon tracker */ init() { this.elements.toolbarEmissions = document.getElementById('toolbar-emissions'); this.elements.moreInfoBtn = document.getElementById('emissions-more-info'); this.elements.emissionsModal = document.getElementById('emissions-modal'); this.elements.okModalBtn = document.getElementById('ok-emissions-btn'); this.elements.closeModalBtn = document.getElementById('close-emissions-btn'); this.elements.emissionsTableBody = document.getElementById('emissions-table-body'); this.elements.totalEmissionsCell = document.getElementById('totalEmissions'); this.elements.totalTokensCell = document.getElementById('totalTokens'); this.elements.totalRepliesCell = document.getElementById('totalReplies'); this.attachEventListeners(); }, attachEventListeners() { this.elements.moreInfoBtn.addEventListener('click', () => this.openModal()); this.elements.okModalBtn.addEventListener('click', () => this.closeModal()); this.elements.closeModalBtn.addEventListener('click', () => this.closeModal()); this.elements.emissionsModal.addEventListener('click', (e) => { if (e.target === this.elements.emissionsModal) { this.closeModal(); } }); }, /** * Format emissions value for display * @param {number} kgCO2eq - The emissions value in kgCO2eq * @returns {string} Formatted string with appropriate unit */ formatEmissions(kgCO2eq) { if (kgCO2eq < 0.001) { return `${(kgCO2eq * 1000000).toFixed(3)} mg`; } else if (kgCO2eq < 1) { return `${(kgCO2eq * 1000).toFixed(3)} g`; } else { return `${kgCO2eq.toFixed(3)} kg`; } }, /** * Format number with thousands separator * @param {number} num - Number to format * @returns {string} Formatted number */ formatNumber(num) { return num.toLocaleString(); }, countReplies(messages) { return messages.filter(msg => msg.role === 'assistant' && msg.content && msg.content !== 'no_reply' ).length; }, /** * Count tokens in messages * @param {Array} messages - Array of message objects * @returns {number} Total token count */ countTokens(messages) { return messages.reduce((total, msg) => { return total + (msg.nTokens || 0); }, 0); }, /** * Update the toolbar emissions display */ updateEmissions() { const totalEmissions = StateManager.getAllEmissions(); this.elements.toolbarEmissions.innerHTML = this.formatEmissions(totalEmissions); }, /** * Populate the emissions table */ populateTable() { this.elements.emissionsTableBody.innerHTML = ''; let totalEmissions = 0; let totalTokens = 0; let totalReplies = 0; // Populate each model row Object.keys(StateManager.modelChats).forEach(modelType => { const chat = StateManager.modelChats[modelType]; const emissions = StateManager.getTotalEmissions(modelType); const tokens = this.countTokens(chat.messages); const replies = this.countReplies(chat.messages); const emissionsPerToken = tokens > 0 ? emissions / tokens : 0; totalEmissions += emissions; totalTokens += tokens; totalReplies += replies; const row = document.createElement('tr'); row.innerHTML = `