add go bench client

This commit is contained in:
Gilles Soulier
2026-01-11 23:41:30 +01:00
parent c67befc549
commit 6abc70cdfe
80 changed files with 13311 additions and 61 deletions

251
frontend/js/icon-manager.js Normal file
View File

@@ -0,0 +1,251 @@
/**
* Icon Manager - Gestion des packs d'icônes
* Permet de basculer entre emojis, FontAwesome, et icônes personnalisées
*/
(function() {
'use strict';
const ICON_PACKS = {
'emoji': {
name: 'Emojis Unicode',
description: 'Emojis colorés par défaut',
icons: {
'add': '',
'edit': '✏️',
'delete': '🗑️',
'save': '💾',
'upload': '📤',
'download': '📥',
'image': '🖼️',
'file': '📄',
'pdf': '📕',
'link': '🔗',
'refresh': '🔄',
'search': '🌍',
'settings': '⚙️',
'close': '❌',
'check': '✅',
'warning': '⚠️',
'info': '',
'copy': '📋'
}
},
'fontawesome-solid': {
name: 'FontAwesome Solid',
description: 'Icônes FontAwesome pleines (bold)',
icons: {
'add': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/plus.svg" aria-hidden="true"></span>',
'edit': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/pen-to-square.svg" aria-hidden="true"></span>',
'delete': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/trash-can.svg" aria-hidden="true"></span>',
'save': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/floppy-disk.svg" aria-hidden="true"></span>',
'upload': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/upload.svg" aria-hidden="true"></span>',
'download': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/download.svg" aria-hidden="true"></span>',
'image': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/image.svg" aria-hidden="true"></span>',
'file': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/file.svg" aria-hidden="true"></span>',
'pdf': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/file-pdf.svg" aria-hidden="true"></span>',
'link': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/link.svg" aria-hidden="true"></span>',
'refresh': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/arrows-rotate.svg" aria-hidden="true"></span>',
'search': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/earth-europe.svg" aria-hidden="true"></span>',
'settings': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/gear.svg" aria-hidden="true"></span>',
'close': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/xmark.svg" aria-hidden="true"></span>',
'check': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/check.svg" aria-hidden="true"></span>',
'warning': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/triangle-exclamation.svg" aria-hidden="true"></span>',
'info': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/circle-info.svg" aria-hidden="true"></span>',
'copy': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/copy.svg" aria-hidden="true"></span>'
}
},
'fontawesome-regular': {
name: 'FontAwesome Regular',
description: 'Icônes FontAwesome fines (outline)',
icons: {
'add': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/square-plus.svg" aria-hidden="true"></span>',
'edit': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/pen-to-square.svg" aria-hidden="true"></span>',
'delete': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/trash-can.svg" aria-hidden="true"></span>',
'save': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/floppy-disk.svg" aria-hidden="true"></span>',
'upload': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/upload.svg" aria-hidden="true"></span>',
'download': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/download.svg" aria-hidden="true"></span>',
'image': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/image.svg" aria-hidden="true"></span>',
'file': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/file.svg" aria-hidden="true"></span>',
'pdf': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/file-pdf.svg" aria-hidden="true"></span>',
'link': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/link.svg" aria-hidden="true"></span>',
'refresh': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/arrows-rotate.svg" aria-hidden="true"></span>',
'search': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/earth-europe.svg" aria-hidden="true"></span>',
'settings': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/gear.svg" aria-hidden="true"></span>',
'close': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/circle-xmark.svg" aria-hidden="true"></span>',
'check': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/circle-check.svg" aria-hidden="true"></span>',
'warning': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/triangle-exclamation.svg" aria-hidden="true"></span>',
'info': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/solid/circle-info.svg" aria-hidden="true"></span>',
'copy': '<span class="btn-icon svg-inline" data-svg-src="icons/svg/fa/regular/copy.svg" aria-hidden="true"></span>'
}
},
'icons8': {
name: 'Icons8 PNG',
description: 'Icônes Icons8 existantes (PNG)',
icons: {
'add': '<img src="icons/icons8-done-48.png" class="btn-icon" alt="Add">',
'edit': '<img src="icons/icons8-edit-pencil-48.png" class="btn-icon" alt="Edit">',
'delete': '<img src="icons/icons8-delete-48.png" class="btn-icon" alt="Delete">',
'save': '<img src="icons/icons8-save-48.png" class="btn-icon" alt="Save">',
'upload': '📤',
'download': '📥',
'image': '<img src="icons/icons8-picture-48.png" class="btn-icon" alt="Image">',
'file': '📄',
'pdf': '📕',
'link': '🔗',
'refresh': '🔄',
'search': '🌍',
'settings': '<img src="icons/icons8-setting-48.png" class="btn-icon" alt="Settings">',
'close': '<img src="icons/icons8-close-48.png" class="btn-icon" alt="Close">',
'check': '<img src="icons/icons8-check-mark-48.png" class="btn-icon" alt="Check">',
'warning': '⚠️',
'info': '',
'copy': '📋'
}
}
};
const DEFAULT_PACK = 'emoji';
const STORAGE_KEY = 'benchtools_icon_pack';
const svgCache = new Map();
function normalizeSvg(svgText) {
let text = svgText;
text = text.replace(/fill="(?!none)[^"]*"/g, 'fill="currentColor"');
text = text.replace(/stroke="(?!none)[^"]*"/g, 'stroke="currentColor"');
return text;
}
function inlineSvgElement(el) {
const src = el.getAttribute('data-svg-src');
if (!src) return;
const cached = svgCache.get(src);
if (cached) {
el.innerHTML = cached;
return;
}
fetch(src)
.then(response => {
if (!response.ok) {
throw new Error(`SVG load failed: ${response.status}`);
}
return response.text();
})
.then(text => {
const normalized = normalizeSvg(text);
svgCache.set(src, normalized);
el.innerHTML = normalized;
})
.catch(error => {
console.warn('[IconManager] Failed to inline SVG:', src, error);
});
}
function inlineSvgIcons(root = document) {
const elements = root.querySelectorAll('[data-svg-src]');
elements.forEach(el => inlineSvgElement(el));
}
// Icon Manager Object
const IconManager = {
packs: ICON_PACKS,
getCurrentPack: function() {
return localStorage.getItem(STORAGE_KEY) || DEFAULT_PACK;
},
applyPack: function(packName) {
if (!ICON_PACKS[packName]) {
console.error(`Icon pack "${packName}" not found`);
return false;
}
localStorage.setItem(STORAGE_KEY, packName);
// Dispatch custom event for icon pack change
window.dispatchEvent(new CustomEvent('iconPackChanged', {
detail: {
pack: packName,
packName: ICON_PACKS[packName].name
}
}));
return true;
},
getIcon: function(iconName, fallback = '?') {
const currentPack = this.getCurrentPack();
const pack = ICON_PACKS[currentPack];
if (!pack) {
console.warn(`Icon pack "${currentPack}" not found`);
return fallback;
}
return pack.icons[iconName] || fallback;
},
getAllPacks: function() {
return Object.keys(ICON_PACKS);
},
getPackInfo: function(packName) {
return ICON_PACKS[packName] || null;
},
// Helper pour générer un bouton avec icône
createButton: function(iconName, text = '', className = 'btn btn-primary') {
const icon = this.getIcon(iconName);
const textPart = text ? ` ${text}` : '';
return `<button class="${className}">${icon}${textPart}</button>`;
},
// Helper pour mettre à jour tous les boutons de la page
updateAllButtons: function() {
// Cette fonction sera appelée après changement de pack
// Pour mettre à jour dynamiquement tous les boutons
const buttons = document.querySelectorAll('[data-icon]');
buttons.forEach(btn => {
const iconName = btn.getAttribute('data-icon');
const iconSpan = btn.querySelector('.btn-icon-wrapper');
if (iconSpan && iconName) {
iconSpan.innerHTML = this.getIcon(iconName);
}
});
inlineSvgIcons();
},
inlineSvgIcons: function(root = document) {
inlineSvgIcons(root);
}
};
// Auto-initialize on load
function initializeIcons() {
IconManager.updateAllButtons();
// Also call utils.js initializeButtonIcons if available
if (window.initializeButtonIcons) {
window.initializeButtonIcons();
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initializeIcons);
} else {
initializeIcons();
}
// Re-initialize when icon pack changes
window.addEventListener('iconPackChanged', function() {
setTimeout(initializeIcons, 100); // Small delay to ensure DOM is ready
});
// Expose globally
window.IconManager = IconManager;
// Log initialization
console.log(`[IconManager] Initialized with pack: ${IconManager.getCurrentPack()}`);
})();