# Componente de Revelar Tags em Listas de Tópicos - Expandir/Recolher tags em listas de tópicos

**URL:** https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072
**Category:** Development
**Tags:** tags
**Created:** [Outubro 18, 2025, 11:33am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072 "2025-10-18T11:33:58Z")
**Posts on this page:** 19
**Page:** 1

<div class="post-metadata">

### Author: ![jrgong](https://avatars.discourse-cdn.com/v4/letter/j/c57346/32.png) [@jrgong](https://meta.discourse.org/u/jrgong)
#### Post date: [Outubro 18, 2025, 11:33am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/1 "2025-10-18T11:33:58Z")

</div>

Nota: Antes de postar isso nos componentes de tema, eu queria obter algum feedback primeiro se este componente de tema se qualifica ou se há algum problema importante com ele.

> ⚠ Divulgação: Este componente de tema foi planejado, implementado e testado com a ajuda de ferramentas de codificação de IA.

Adoraria ouvir seu feedback!

* * *

| | | |
| --- | --- | --- |
| ℹ | **Resumo** | Revelação de Tags |
| 👓 | **Prévia** | Não disponível… |
| 🛠 | **Repositório** | [GitHub - jrgong420/discourse-tag-reveal](https://github.com/jrgong420/discourse-tag-reveal) |
| ❓ | **Guia de Instalação** | [Como instalar um tema ou componente de tema](https://meta.discourse.org/t/how-do-i-install-a-theme-or-theme-component/63682) |
| 📖 | **Novo em Temas do Discourse?** | [Guia para iniciantes no uso de Temas do Discourse](https://meta.discourse.org/t/beginners-guide-to-using-discourse-themes/91966) |

Discourse Tag Reveal é um componente de tema leve que mantém as listas de tópicos organizadas, mostrando apenas as primeiras N tags por tópico e substituindo as restantes por um alternador acessível " +X mais tags ". Os usuários podem expandir para ver todas as tags e recolher de volta para a visualização encurtada. Ele funciona imediatamente com a interface de tags padrão do Discourse e não requer alterações no lado do servidor.

## Funcionalidades

- Limite de tags configurável (padrão: 5) através das configurações do tema

- Alternador estilizado como uma tag, acessível por teclado (Enter/Espaço) com atributos ARIA

- Strings localizadas usando themePrefix e discourse-i18n

- Comportamento seguro para SPA: redefine e reaplica a lógica em mudanças de página

- Suporta rolagem infinita via MutationObserver

- CSS mínimo; respeita os estilos de tag principais

- Sem substituições de template ou dependências de plugin

## Capturas de Tela / Demonstração

…em breve

## Instalação e Configuração

- Testado com a versão do Discourse: 3.6.0beta1

- Configure as configurações na aba Configurações do componente:

- `max_tags_visible` (inteiro, padrão 5): Quantas tags mostrar antes de recolher

- `toggle_tag_style`: Estilo visual do alternador para corresponder à aparência da tag (Atualmente apenas o estilo “box” implementado)

- Escopo: afeta as listas de tópicos (listas de tópicos Mais recentes, Novas, Não lidas e de categorias)

## Compatibilidade com outros Componentes de Tema

⚠ Apenas testes mínimos realizados, por favor, teste você mesmo antes de implantar em produção

- [Topic Cards](https://meta.discourse.org/t/topic-cards/296048) ✅

## Notas

- Certifique-se de que a marcação esteja habilitada (Admin → Configurações → Tags), caso contrário, você não verá nenhum efeito

- Se o seu site personalizar pesadamente o CSS das tags, você pode querer ajustar os estilos `.ts-toggle` para um alinhamento visual perfeito

## Ideias para o futuro

Eu realmente não planejo implementar mais recursos, mas ficarei feliz em aceitar PRs. Algumas ideias para o futuro:

- Habilitar/desabilitar para tags na visualização de tópicos

- Controle granular para páginas e/ou categorias específicas

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [Outubro 18, 2025, 12:01pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/2 "2025-10-18T12:01:07Z")

</div>

> [@jrgong](#):
>
> `max_tags_per_topic`

Você nomeou deliberadamente a configuração com o mesmo nome de uma configuração no núcleo? Eu estaria preocupado com mal-entendidos.

 ![Screenshot_20251018_140027_Firefox](https://global.discourse-cdn.com/meta/original/4X/e/1/c/e1c378b9d8ed5cc3f35e34690b400ce321de5145.jpeg)

---

<div class="post-metadata">

### Author: ![jrgong](https://avatars.discourse-cdn.com/v4/letter/j/c57346/32.png) [@jrgong](https://meta.discourse.org/u/jrgong)
#### Post date: [Outubro 18, 2025, 12:27pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/3 "2025-10-18T12:27:46Z")

</div>

> [@Moin](#):
>
> Você nomeou deliberadamente a configuração com o mesmo nome de uma configuração no núcleo? Eu estaria preocupado com mal-entendidos.

boa observação! Acabei de modificar…

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [Outubro 18, 2025, 2:14pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/4 "2025-10-18T14:14:37Z")

</div>

Parece muito interessante. Vou tentar no meu ambiente de desenvolvimento mais tarde, já que não parece funcionar no Theme Creator (a menos que eu esteja fazendo algo errado?) 🤔.

---

<div class="post-metadata">

### Author: ![mcwumbly](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/mcwumbly/32/103861_2.png) [@mcwumbly](https://meta.discourse.org/u/mcwumbly)
#### Post date: [Outubro 18, 2025, 4:10pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/5 "2025-10-18T16:10:36Z")

</div>

Parece interessante! Você poderia compartilhar algumas capturas de tela ou gravações de tela do recurso em ação?

---

<div class="post-metadata">

### Author: ![jrgong](https://avatars.discourse-cdn.com/v4/letter/j/c57346/32.png) [@jrgong](https://meta.discourse.org/u/jrgong)
#### Post date: [Outubro 18, 2025, 5:31pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/6 "2025-10-18T17:31:52Z")

</div>

😃 Adicionei uma demonstração rápida em vídeo na primeira postagem, veja aqui:

> [@jrgong](#):
>
> ## Demonstração em Vídeo:
> 
> ![CleanShot 2025-10-18 at 19.28.39](https://global.discourse-cdn.com/meta/original/4X/3/d/a/3da7e5e98a602a5442bdd8ad577e301802171cbf.gif)

> [@NateDhaliwal](#):
>
> Parece bem interessante. Vou testar no meu ambiente de desenvolvimento mais tarde, já que não parece funcionar no Theme Creator (a menos que eu esteja fazendo algo errado?) 🤔.

Eu nem verifiquei como enviar/adicionar meu componente do TC lá…😃  
Mas de qualquer forma, prefiro reunir alguns feedbacks aqui primeiro, e quando estiver pronto para ser publicado em #customização:tema-componente, eu verei como adicioná-lo lá.

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [Outubro 18, 2025, 5:47pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/7 "2025-10-18T17:47:55Z")

</div>

> [@NateDhaliwal](#):
>
> não parece funcionar no Theme Creator

O Theme Creator não usa o estilo de caixa

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [Outubro 18, 2025, 5:58pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/8 "2025-10-18T17:58:20Z")

</div>

> <https://github.com/jrgong420/discourse-tag-reveal/blob/19ea982ff7eda93cb369b2af947dd23b847af961/locales/en.yml#L9-L9>

Você pode querer usar

```plaintext
more_tags:
  one: "+%{count} tag a mais"
  other: "+%{count} tags a mais"

```

---

<div class="post-metadata">

### Author: ![jrgong](https://avatars.discourse-cdn.com/v4/letter/j/c57346/32.png) [@jrgong](https://meta.discourse.org/u/jrgong)
#### Post date: [Outubro 18, 2025, 6:24pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/9 "2025-10-18T18:24:54Z")

</div>

ponto válido. Esqueci de alterar o rótulo padrão para `+%{count} a mais` para mantê-lo curto e conciso, é assim que o usamos e mantemos as coisas compactas e limpas.

---

<div class="post-metadata">

### Author: ![Arkshine](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/arkshine/32/298682_2.png) [@Arkshine](https://meta.discourse.org/u/Arkshine)
#### Post date: [Outubro 19, 2025, 3:57am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/10 "2025-10-19T03:57:59Z")

</div>

Olá,

Este recurso pode ser interessante em algumas situações!

À primeira vista, há algumas coisas a observar:

- As configurações de tema e as configurações do site não são as mesmas. Você precisa recuperar o serviço primeiro para acessar `max_tags_per_topic`, por exemplo: `const siteSettings = api.container.lookup(\"service:site-settings\");`

- As verificações extras para obter o limite não deveriam ser necessárias; você pode obter o valor diretamente. Provavelmente você pode fazer `Math.min(settings.max_tags_visible, siteSettings.max_tags_per_topic )`

- Você não está restaurando a visibilidade dos separadores.

- Você pode querer cancelar o registro dos eventos

- O processo na carga inicial não deve ser necessário com MutationObserver. Geralmente, antes de se tornar global, você gostaria de verificar primeiro se há uma maneira de reduzir o escopo em torno do elemento usando a API (saída de plugin, por exemplo).

Vou verificar se há uma maneira diferente!

---

<div class="post-metadata">

### Author: ![NateDhaliwal](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/natedhaliwal/32/313494_2.png) [@NateDhaliwal](https://meta.discourse.org/u/NateDhaliwal)
#### Post date: [Outubro 19, 2025, 6:05am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/11 "2025-10-19T06:05:30Z")

</div>

> [@Arkshine](#):
>
> const siteSettings = api.container.lookup(“service:site-settings”);

Como está no arquivo api-initializers, `@service siteSettings` também funcionaria?

---

<div class="post-metadata">

### Author: ![jrgong](https://avatars.discourse-cdn.com/v4/letter/j/c57346/32.png) [@jrgong](https://meta.discourse.org/u/jrgong)
#### Post date: [Outubro 19, 2025, 11:33am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/12 "2025-10-19T11:33:29Z")

</div>

Pode verificar agora? O último commit deve ter corrigido os pontos abordados

---

<div class="post-metadata">

### Author: ![Moin](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/moin/32/554653_2.png) [@Moin](https://meta.discourse.org/u/Moin)
#### Post date: [Outubro 19, 2025, 12:52pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/13 "2025-10-19T12:52:55Z")

</div>

A versão mínima do Discourse 3.6.0 significa que levará um bom tempo até que alguém possa usá-la. Você quis dizer 3.5.0 ou 3.6.0beta1?

---

<div class="post-metadata">

### Author: ![jrgong](https://avatars.discourse-cdn.com/v4/letter/j/c57346/32.png) [@jrgong](https://meta.discourse.org/u/jrgong)
#### Post date: [Outubro 19, 2025, 12:54pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/14 "2025-10-19T12:54:34Z")

</div>

Eu quis dizer 3.6.0beta1, essa é a versão com a qual tenho testado…

---

<div class="post-metadata">

### Author: ![Arkshine](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/arkshine/32/298682_2.png) [@Arkshine](https://meta.discourse.org/u/Arkshine)
#### Post date: [Outubro 20, 2025, 5:37am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/15 "2025-10-20T05:37:23Z")

</div>

> [@NateDhaliwal](#):
>
> Como está no arquivo api-initializers, o `@service siteSettings` também funcionaria?

Você usa isso em uma classe. Não funcionará de outra forma.

> [@jrgong](#):
>
> Eu quis dizer 3.6.0beta1, essa é a versão com a qual tenho testado…

Você quer escrever `3.6.0.beta1` então, caso contrário, ninguém poderá instalá-lo no momento.

> [@Arkshine](#):
>
> Deixe-me ver se há uma maneira diferente!

Eu verifiquei um pouco. De fato, não há uma maneira direta de conseguir isso; no entanto, encontrei um método interessante e simplificado para fazer isso usando a API.

- Ele usa o modelo de tópico para alterar quais tags visíveis serão geradas antes que o template seja gerado. Isso significa que não há manipulação de DOM e é independente das configurações. Dependendo do estado (revealTags), ele retornará a lista original ou uma parcial.

- Para criar o botão de alternância, ele usa a API para adicionar uma tag com o HTML de um botão (infelizmente, não há um plugin outlet aqui). O evento de clique é tratado separadamente. Ao clicar, o estado de alternância é atualizado (revealTags) e acionamos uma nova renderização da lista de tags.

A grande vantagem dessa abordagem é que você não precisa mexer no HTML e descobrir o que mostrar/ocultar com CSS, com base nos diferentes estilos.

![chrome_lSKqwYt5Z7](https://global.discourse-cdn.com/meta/original/4X/0/8/6/086fcc2239793f483013587afd34c781cd7fd9ad.gif)

Compartilhando meu código de teste aqui:

```js
import { apiInitializer } from "discourse/lib/api";
import { i18n } from "discourse-i18n";
import { computed } from "@ember/object";

export default apiInitializer((api) => {
  const siteSettings = api.container.lookup("service:site-settings");

  const maxVisibleTags = Math.min(
    settings.max_tags_visible,
    siteSettings.max_tags_per_topic
  );

  let topicModels = {};

  api.modifyClass(
    "model:topic",
    (Superclass) =>
      class extends Superclass {
        revealTags = false;

        init() {
          super.init(...arguments);
          topicModels[this.id] = this;
        }

        @computed("tags")
        get visibleListTags() {
          if (this.revealTags) {
            return super.visibleListTags;
          }
          return super.visibleListTags.slice(0, maxVisibleTags);
        }
      }
  );

  api.addTagsHtmlCallback(
    (topic, params) => {
      if (topic.tags.length <= maxVisibleTags) {
        return "";
      }

      const isExpanded = topic.revealTags;
      const label = isExpanded
        ? i18n(themePrefix("js.tag_reveal.hide"))
        : i18n(themePrefix("js.tag_reveal.more_tags"), {
            count: topic.tags.length - maxVisibleTags,
          });

      return `<a class="reveal-tag-action" role="button" aria-expanded="${isExpanded}">${label}</a>`;
    },
    {
      priority: siteSettings.max_tags_per_topic + 1,
    }
  );

  document.addEventListener("click", (event) => {
    const target = event.target;
    if (!target?.matches(".reveal-tag-action")) {
      return;
    }

    event.preventDefault();
    event.stopPropagation();

    const element =
      target.closest("[data-topic-id]") ||
      document.querySelector("h1[data-topic-id]");
    const topicId = element?.dataset.topicId;
    if (!topicId) {
      return;
    }

    const topicModel = topicModels[topicId];
    if (!topicModel) {
      return;
    }

    topicModel.revealTags = !topicModel.revealTags;
    topicModel.notifyPropertyChange("tags");
  });
});

```

```css
.reveal-tag-action {
  background-color: var(--primary-50);
  border: 1px solid var(--primary-200);
  color: var(--primary-800);
  font-size: small;
  padding-inline: 3px;
}

.discourse-tags__tag-separator:has(+ .reveal-tag-action) {
  visibility: hidden;
}

```

---

<div class="post-metadata">

### Author: ![jrgong](https://avatars.discourse-cdn.com/v4/letter/j/c57346/32.png) [@jrgong](https://meta.discourse.org/u/jrgong)
#### Post date: [Outubro 20, 2025, 9:15am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/16 "2025-10-20T09:15:49Z")

</div>

Olá pessoal, fiz outra atualização e adicionei recursos experimentais adicionais (“tags em destaque” que sempre vêm primeiro e não são calculadas para a quantidade máxima + linha de tópico em destaque na visualização da lista de tópicos), então o TC geral está mudando um pouco com mais funcionalidades estendidas para destacar certos com base em guias configuradas.

@Arkshine obrigado por compartilhar seu método simplificado, eu realmente aprecio!!!

Também afetou a visualização de tópico único, então adicionamos uma configuração para habilitar esse comportamento manualmente. Além disso, com o novo método, o estado expandido persiste ao navegar para uma rota/página diferente, mas ainda não abordei isso.

Eu o implementei neste branch:

> **[GitHub - jrgong420/discourse-tag-reveal at feature/model-based-tag-reveal](https://github.com/jrgong420/discourse-tag-reveal/tree/feature/model-based-tag-reveal)**
>
> feature/model-based-tag-reveal

---

<div class="post-metadata">

### Author: ![Arkshine](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/arkshine/32/298682_2.png) [@Arkshine](https://meta.discourse.org/u/Arkshine)
#### Post date: [Outubro 21, 2025, 3:47am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/17 "2025-10-21T03:47:43Z")

</div>

- Acho que você precisa revisar o CSS.

---

<div class="post-metadata">

### Author: ![jrgong](https://avatars.discourse-cdn.com/v4/letter/j/c57346/32.png) [@jrgong](https://meta.discourse.org/u/jrgong)
#### Post date: [Outubro 22, 2025, 7:00am UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/18 "2025-10-22T07:00:32Z")

</div>

> [@Arkshine](#):
>
> você provavelmente não deveria adicionar `discourse-tag` ao botão de alternância, não é uma tag.

na verdade foi intencional. por exemplo, o plugin de votação de tópicos usa a classe nos elementos “x votos”.

→ veja em ação na categoria [Feature - Discourse Meta](https://meta.discourse.org/c/feature/2/l/latest?order=votes)

> [@Arkshine](#):
>
> - não use a classe `box` nele também, isso bagunça o estilo da lista (eu usei “-box”)

Obrigado, vou verificar!

> [@Arkshine](#):
>
> a configuração tem apenas o valor “box”, talvez você pudesse adicionar “none”, para que se encaixe melhor no estilo de lista/marcador.

Por enquanto, implementei apenas o estilo de caixa, pois é o que estou usando em nossa instância do Discourse. Adicionarei os estilos ausentes mais tarde (aceito PRs 😉 )

---

<div class="post-metadata">

### Author: ![Arkshine](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/arkshine/32/298682_2.png) [@Arkshine](https://meta.discourse.org/u/Arkshine)
#### Post date: [Outubro 22, 2025, 4:58pm UTC](https://meta.discourse.org/t/asking-for-feedback-tag-reveal-component-in-topic-lists-expand-collapse-tags-in-topic-lists/386072/19 "2025-10-22T16:58:07Z")

</div>

> [@jrgong](#):
>
> na verdade foi intencional. por exemplo, o plugin de votação de tópicos usa a classe nos elementos “x votos”.

Entendo. Acho que faz sentido se você exibir informações como uma tag, mas aqui é um botão para exibir mais tags; o contexto é diferente para mim. A decisão é sua; não acho que isso importe muito.

Para continuar com o feedback:

- A lista de tags pode ser exibida em outros lugares, como: página de categorias, atividades do usuário, etc. Eu provavelmente removeria a configuração `collapse_in_topic_view` e criaria uma nova com rotas específicas ou simplesmente a habilitaria em todos os lugares.  
Na minha versão de teste, usei algo assim para ignorar outras rotas:

> **JS**
>
> ```js
> function isAllowedRoute(routeName) {
> const fullRoutesName = [
> "index",
> "userActivity.topics",
> "userActivity.read",
> ...siteSettings.top_menu.split("|").map((item) => `discovery.${item}`),
> ];
> 
> const partialRoutesName = ["topic."];
> 
> if (
> fullRoutesName.includes(routeName) ||
> partialRoutesName.some((partial) => routeName.startsWith(partial))
> ) {
> return true;
> }
> 
> return false;
> }
> 
> ```

- A injeção de CSS pode ser substituída usando a API para adicionar uma classe a `topic-list-item` e a uma tag, então você move o CSS para `common.css`.

Por exemplo:

> **JS**
>
> ````
> ```js
> import { defaultRenderTag } from "discourse/lib/render-tag";
> 
> api.registerValueTransformer(
> "topic-list-item-class",
> ({ value, context }) => {
> if (highlightedTagsSet.size === 0) {
> return value;
> }
> 
> if (context.topic?.tags?.some((tag) => highlightedTagsSet.has(tag))) {
> return [...value, `highlighted-tag__${settings.highlighted_style}`];
> }
> 
> return value;
> }
> );
> 
> api.replaceTagRenderer((tag, params) => {
> if (highlightedTagsSet.has(tag)) {
> params.extraClass = params.extraClass || "";
> params.extraClass += "highlighted";
> }
> 
> return defaultRenderTag(tag, params);
> });
> ```
> 
> ````

> **CSS**
>
> ```css
> /* Hides the last separator before the toggle button */
> .discourse-tag__tag-separator:has(+ .reveal-tag-action) {
> visibility: hidden;
> }
> 
> .reveal-tag-action {
> color: var(--primary-500);
> 
> &.-box {
> background-color: var(--primary-50);
> outline: 1px solid var(--primary-200);
> padding-inline: 8px;
> }
> }
> 
> .latest-topic-list-item,
> .topic-list-item {
> .discourse-tag.highlighted {
> color: var(--tertiary);
> border-color: var(--tertiary);
> background: color-mix(in srgb, var(--tertiary) 12%, transparent);
> font-weight: 600;
> }
> }
> 
> &.highlighted-tag {
> &__left-border {
> border-left: 3px solid var(--tertiary);
> background: color-mix(in srgb, var(--tertiary) 6%, transparent);
> transition: box-shadow 160ms ease;
> 
> &:hover {
> box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
> }
> }
> 
> &__outline {
> outline: 1px solid var(--tertiary);
> outline-offset: -2px;
> border-radius: 7px;
> background: color-mix(in srgb, var(--tertiary) 5%, transparent);
> box-shadow: 0 1px 6px rgba(0, 0, 0, 0.06);
> transition: background-color 160ms ease;
> }
> 
> &__card {
> border-left: 3px solid var(--tertiary);
> background: var(--tertiary-very-low);
> border-radius: var(--border-radius);
> padding-block: var(--space-2);
> box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
> transition: box-shadow 160ms ease;
> 
> &:hover {
> box-shadow: 0 3px 12px rgba(0, 0, 0, 0.1);
> }
> }
> }
> }
> 
> ```

- Você não precisa definir a rota atual de `onPageChange`, você pode acessá-la a partir do roteador.
- Tenha cuidado com a capitalização das tags. Você tem configurações de site que não impõem letras minúsculas, então acho que é melhor não modificar a tag.
- Sobre redefinir o estado, você provavelmente pode usar `onPageChange`.

> **JS**
>
> ````
> ```js
> api.onPageChange((url) => {
> const route = api.container.lookup("service:router").recognize(url);
> if (!isAllowedRoute(route?.name)) {
> return;
> }
> 
> for (const [id, model] of topicModels) {
> if (model && model.revealTags) {
> model.revealTags = false;
> model.notifyPropertyChange("tags");
> }
> }
> });
> ```
> 
> ````

- Se possível, seria ótimo adicionar testes.

Aqui está o código de teste completo (fiz outras pequenas alterações)

> **JS**
>
> ```js
> import { computed } from "@ember/object";
> import { apiInitializer } from "discourse/lib/api";
> import { i18n } from "discourse-i18n";
> import { defaultRenderTag } from "discourse/lib/render-tag";
> import { service } from "@ember/service";
> 
> export default apiInitializer((api) => {
> const siteSettings = api.container.lookup("service:site-settings");
> const router = api.container.lookup("service:router");
> 
> const maxVisibleTags = Math.min(
> settings.max_tags_visible,
> siteSettings.max_tags_per_topic
> );
> 
> const highlightedTagsSet = new Set(settings.highlighted_tags.split("|"));
> const topicModels = new Map();
> 
> function isAllowedRoute(routeName) {
> const fullRoutesName = [
> "index",
> "userActivity.topics",
> "userActivity.read",
> "tag.show",
> ...siteSettings.top_menu.split("|").map((item) => `discovery.${item}`),
> ];
> 
> const partialRoutesName = ["topic."];
> 
> if (
> fullRoutesName.includes(routeName) ||
> partialRoutesName.some((partial) => routeName.startsWith(partial))
> ) {
> return true;
> }
> 
> return false;
> }
> 
> api.modifyClass(
> "model:topic",
> (Superclass) =>
> class extends Superclass {
> @service router;
> 
> revealTags = false;
> 
> init() {
> super.init(...arguments);
> topicModels.set(String(this.id), this);
> }
> 
> willDestroy() {
> super.willDestroy(...arguments);
> topicModels.delete(String(this.id));
> }
> 
> @computed("tags")
> get visibleListTags() {
> const baseTags = super.visibleListTags || [];
> 
> if (!isAllowedRoute(this.router.currentRouteName)) {
> return baseTags;
> }
> 
> const highlightedList = [];
> const regularList = [];
> 
> baseTags.forEach((tag) => {
> if (highlightedTagsSet.has(tag)) {
> highlightedList.push(tag);
> } else {
> regularList.push(tag);
> }
> });
> 
> if (this.revealTags) {
> return [...highlightedList, ...regularList];
> }
> 
> return [...highlightedList, ...regularList.slice(0, maxVisibleTags)];
> }
> }
> );
> 
> api.addTagsHtmlCallback(
> (topic) => {
> if (!isAllowedRoute(topic.router.currentRouteName)) {
> return "";
> }
> 
> const allTags = topic.tags || [];
> if (allTags.length === 0) {
> return "";
> }
> 
> const highlightedCount = allTags.filter((tag) =>
> highlightedTagsSet.has(tag)
> ).length;
> const regularCount = allTags.length - highlightedCount;
> const effectiveLimit =
> highlightedCount + Math.min(regularCount, maxVisibleTags);
> 
> // Only show toggle if there are hidden tags
> if (allTags.length <= effectiveLimit) {
> return "";
> }
> 
> const isExpanded = topic.revealTags;
> const hiddenCount = allTags.length - effectiveLimit;
> const label = isExpanded
> ? i18n(themePrefix("js.tag_reveal.hide"))
> : i18n(themePrefix("js.tag_reveal.more_tags"), {
> count: hiddenCount,
> });
> 
> const classList = ["discourse-tag", "reveal-tag-action"];
> if (settings.toggle_tag_style === "box") {
> classList.push("-box");
> }
> 
> return `<a class="${classList.join(" ")}" role="button" aria-expanded="${isExpanded}">${label}</a>`;
> },
> {
> priority: siteSettings.max_tags_per_topic + 1,
> }
> );
> 
> api.registerValueTransformer(
> "topic-list-item-class",
> ({ value, context }) => {
> if (highlightedTagsSet.size === 0) {
> return value;
> }
> 
> if (context.topic?.tags?.some((tag) => highlightedTagsSet.has(tag))) {
> return [...value, `highlighted-tag__${settings.highlighted_style}`];
> }
> 
> return value;
> }
> );
> 
> api.replaceTagRenderer((tag, params) => {
> let newParams = params;
> 
> if (highlightedTagsSet.has(tag)) {
> newParams = {
> ...params,
> extraClass: [params.extraClass, "highlighted"]
> .filter(Boolean)
> .join(" "),
> };
> }
> 
> return defaultRenderTag(tag, newParams);
> });
> 
> document.addEventListener(
> "click",
> (event) => {
> const target = event.target;
> if (!target?.matches(".reveal-tag-action")) {
> return;
> }
> 
> event.preventDefault();
> event.stopPropagation();
> 
> const element =
> target.closest("[data-topic-id]") ||
> document.querySelector("h1[data-topic-id]");
> const topicId = element?.dataset.topicId;
> if (!topicId) {
> return;
> }
> 
> const topicModel = topicModels.get(topicId);
> if (!topicModel) {
> return;
> }
> 
> topicModel.revealTags = !topicModel.revealTags;
> topicModel.notifyPropertyChange("tags");
> },
> true
> );
> 
> api.onPageChange((url) => {
> const route = api.container.lookup("service:router").recognize(url);
> if (!isAllowedRoute(route?.name)) {
> return;
> }
> 
> for (const [id, model] of topicModels) {
> if (model && model.revealTags) {
> model.revealTags = false;
> model.notifyPropertyChange("tags");
> }
> }
> });
> });
> 
> ```
