Con la ayuda de ChatGPT y Claude, pude hacerlo funcionar y ser muy personalizable:
Si quieres hacerlo, crea un nuevo componente y añade esto a la pestaña CSS:
.sidebar-section-link-prefix .emoji.prefix-emoji {
width: 1rem !important;
height: 1rem !important;
}
En mi caso particular, 1rem funciona muy bien. Ajústalo a tu foro/comunidad.
Luego, en la pestaña JS:
import { apiInitializer } from "discourse/lib/api";
import { emojiUrlFor } from "discourse/lib/text";
export default apiInitializer("0.11.1", (api) => {
// Mapear nombres de sección a IDs
const sectionIds = {
"community": 1,
"tiago": 2,
"personal-section": 3,
// Añadir más secciones aquí
};
// Mapa de [sectionId, itemName] a nombres de emoji
const iconReplacements = {
// Sección Comunidad (ID: 1)
"1,admin": "wrench",
"1,review": "triangular_flag_on_post",
"1,everything": "books",
"1,my-posts": "writing_hand",
"1,my-messages": "envelope_with_arrow",
// Sección Tiago (ID: 2)
"2,Journal": "notebook_with_decorative_cover",
"2,Music": "musical_note",
"2,About": "bust_in_silhouette",
// Sección Personal (ID: 3)
"3,Backups": "floppy_disk",
"3,Scheduled": "clock3",
"3,Staff": "lock",
"3,Components": "electric_plug",
};
function replaceIcons() {
Object.entries(sectionIds).forEach(([sectionName, sectionId]) => {
Object.entries(iconReplacements).forEach(([key, emojiName]) => {
const [keyId, itemName] = key.split(',');
// Procesar solo si este reemplazo es para la sección actual
if (parseInt(keyId) === sectionId) {
const url = emojiUrlFor(emojiName);
// Intentar múltiples selectores para capturar tanto el menú hamburguesa como la barra lateral fija
const selectors = [
// Selector de barra lateral fija
`div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
// Selector de menú hamburguesa (más específico)
`.menu-panel div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
// Fallback genérico
`[data-section-name="${sectionName}"] [data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`
];
for (const selector of selectors) {
const prefix = document.querySelector(selector);
if (prefix && url) {
// Comprobar si ya ha sido reemplazado para evitar manipulación innecesaria del DOM
if (!prefix.querySelector('.prefix-emoji')) {
prefix.innerHTML = `
<img src="${url}"
title="${emojiName}"
alt="${emojiName}"
class="emoji prefix-emoji">
`;
}
break; // Salir del bucle una vez que encontremos y reemplacemos el icono
}
}
}
});
});
}
// Ejecutar al cambiar de página
api.onPageChange(replaceIcons);
// También ejecutar con un retraso para capturar contenido cargado dinámicamente
api.onPageChange(() => {
setTimeout(replaceIcons, 100);
setTimeout(replaceIcons, 500);
});
// MutationObserver mejorado para capturar cambios en la barra lateral
const observer = new MutationObserver((mutations) => {
let shouldReplace = false;
mutations.forEach((mutation) => {
// Observar nodos añadidos (funcionalidad original)
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.classList?.contains('menu-panel') ||
node.querySelector?.('.sidebar-sections') ||
node.classList?.contains('sidebar-sections') ||
node.querySelector?.('[data-section-name]')) {
shouldReplace = true;
}
}
});
// Observar cambios de atributos en secciones de la barra lateral (colapsar/expandir)
if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {
const target = mutation.target;
if (target.matches('[data-section-name]') ||
target.closest('[data-section-name]') ||
target.matches('.sidebar-section') ||
target.closest('.sidebar-section')) {
shouldReplace = true;
}
}
// Observar cambios en childList en secciones de la barra lateral
if (mutation.type === 'childList' && mutation.target.nodeType === Node.ELEMENT_NODE) {
const target = mutation.target;
if (target.matches('[data-section-name]') ||
target.closest('[data-section-name]') ||
target.querySelector('[data-section-name]')) {
shouldReplace = true;
}
}
});
if (shouldReplace) {
setTimeout(replaceIcons, 50);
}
});
// Iniciar la observación con opciones mejoradas
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true, // Observar cambios de atributos
attributeFilter: ['class', 'style', 'aria-expanded'], // Atributos comunes que cambian al colapsar/expandir
});
// Escuchadores de eventos adicionales para interacciones comunes de la barra lateral
document.addEventListener('click', (event) => {
// Comprobar si el clic fue en un encabezado de sección de la barra lateral o un botón de alternancia
if (event.target.closest('.sidebar-section-header') ||
event.target.closest('[data-section-name]') ||
event.target.matches('.sidebar-section-toggle')) {
setTimeout(replaceIcons, 100);
}
});
});
Lo hice de manera que pueda añadir fácilmente nuevas secciones personalizadas por nombre, asignándole un id y luego creando el mapeo de cada sección.
La razón de esto es que puedo tener 2 secciones con el título Journal, por ejemplo, pero tal vez quiero 2 emojis diferentes para cada una.
Estoy bastante seguro de que alguien dirá que hay un componente o plugin para esto
pero aun así fue divertido ir y venir con ChatGPT y Claude hasta que todo funcionó como quería.
Espero que esto ayude a otros usuarios.
Si ves algo que se pueda ajustar/mejorar, por favor compártelo 