Com a ajuda do ChatGPT e do Claude, consegui fazer funcionar e ser muito personalizável:
Se você quiser fazer isso, crie um novo componente e adicione isto à aba CSS:
.sidebar-section-link-prefix .emoji.prefix-emoji {
width: 1rem !important;
height: 1rem !important;
}
No meu caso particular, 1rem funciona muito bem. Ajuste para o seu fórum/comunidade
Em seguida, na aba JS:
import { apiInitializer } from "discourse/lib/api";
import { emojiUrlFor } from "discourse/lib/text";
export default apiInitializer("0.11.1", (api) => {
// Mapeia nomes de seções para IDs
const sectionIds = {
"community": 1,
"tiago": 2,
"personal-section": 3,
// Adicione mais seções aqui
};
// Mapeamento de [sectionId, itemName] para nomes de emoji
const iconReplacements = {
// Seção da comunidade (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",
// Seção Tiago (ID: 2)
"2,Journal": "notebook_with_decorative_cover",
"2,Music": "musical_note",
"2,About": "bust_in_silhouette",
// Seção Pessoal (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(',');
// Processa apenas se esta substituição for para a seção atual
if (parseInt(keyId) === sectionId) {
const url = emojiUrlFor(emojiName);
// Tenta vários seletores para capturar o menu hambúrguer e a barra lateral fixa
const selectors = [
// Seletor da barra lateral fixa
`div[data-section-name="${sectionName}"] li[data-list-item-name="${itemName}"] .sidebar-section-link-prefix.icon`,
// Seletor do menu hambúrguer (mais 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) {
// Verifica se já foi substituído para evitar manipulação desnecessária do DOM
if (!prefix.querySelector('.prefix-emoji')) {
prefix.innerHTML = `
<img src="${url}"
title="${emojiName}"
alt="${emojiName}"
class="emoji prefix-emoji">
`;
}
break; // Sai do loop assim que encontrarmos e substituirmos o ícone
}
}
}
});
});
}
// Executa na mudança de página
api.onPageChange(replaceIcons);
// Também executa com atraso para capturar conteúdo carregado dinamicamente
api.onPageChange(() => {
setTimeout(replaceIcons, 100);
setTimeout(replaceIcons, 500);
});
// MutationObserver aprimorado para capturar alterações na barra lateral
const observer = new MutationObserver((mutations) => {
let shouldReplace = false;
mutations.forEach((mutation) => {
// Observa nós adicionados (funcionalidade 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;
}
}
});
// Observa alterações de atributos nas seções da barra lateral (fechar/abrir)
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;
}
}
// Observa alterações de childList em seções da 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);
}
});
// Inicia a observação com opções aprimoradas
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true, // Observa alterações de atributos
attributeFilter: ['class', 'style', 'aria-expanded'], // Atributos comuns que mudam ao fechar/abrir
});
// Ouvintes de eventos adicionais para interações comuns da barra lateral
document.addEventListener('click', (event) => {
// Verifica se o clique foi em um cabeçalho de seção da barra lateral ou botão de alternância
if (event.target.closest('.sidebar-section-header') ||
event.target.closest('[data-section-name]') ||
event.target.matches('.sidebar-section-toggle')) {
setTimeout(replaceIcons, 100);
}
});
});
Eu fiz de forma que eu possa adicionar facilmente novas seções personalizadas por nome, atribuindo um id e, em seguida, criando o mapeamento de cada seção.
O motivo para isso é que eu posso ter 2 seções com o título Journal, por exemplo, mas talvez eu queira 2 emojis diferentes para cada uma.
Tenho certeza de que haverá alguém dizendo que existe um componente ou plugin para isso
mas ainda assim foi divertido ir e voltar com o ChatGPT e o Claude até que tudo funcionasse como eu queria.
Espero que isso ajude outros usuários.
Se você vir algo que possa ser ajustado/melhorado, por favor, compartilhe 