필터(가나다순)와 문서 보기 조합 가능할까요?

필터를 사용하여 문서 보기와 A-Z(알파벳 순서)로 카테고리를 표시할 수 있을지 궁금합니다.

주제 보기를 정렬하여 표시해야 하는 용어집(glossary)이 있습니다. :slight_smile:

현재 필터 보기는 문서(docs)와 분리되어 있습니다:

이를 위해 커스텀 Theme Component를 사용했습니다:

// A-Z 정렬에는 전체 카테고리가 필요합니다: 코어는 주제 목록을 제목으로 정렬할 수 없기 때문입니다
// (TopicQuery::SORTABLE_MAPPING에 "title"이 없으므로), 정렬 전에 페이지네이션된 목록을
// 모두 불러와야 합니다. 상한선이 설정되어 있습니다 — 문서 카테고리는 작으므로, 더 큰 경우
// 로드된 항목만 정렬하며, 아래 MutationObserver가 이를 최신 상태로 유지합니다.
// ponytail: 코어가 서버 측 제목 정렬을 제공하게 되면 이 전체 루프를 제거하세요.
const MAX_DOC_PAGES = 12; // 페이지당 30개 기준 약 360개 주제
let docLoadRun = 0;

async function loadAllDocTopics(api) {
  const run = ++docLoadRun;

  // `controller:discovery/topics`는 현재 Discourse에서 비추천(deprecated)되었습니다
  // (비추천 ID: discourse.discovery-topics-controller). Discovery는 이제 필터별로
  // 전용 컨트롤러를 사용합니다. 문서 카테고리는 항상 최신 주제 목록이므로 해당 컨트롤러를
  // 직접 조회합니다. 여기서 실패하더라도 첫 페이지만 정렬되므로 모든 단계는 선택 체이닝(optional-chaining)을 사용합니다.
  const list = api.container.lookup("controller:discovery/latest")?.model;
  if (!list?.loadMore) {
    return;
  }

  for (let page = 0; page < MAX_DOC_PAGES; page++) {
    if (!list.more_topics_url) {
      return;
    }

    const before = list.topics?.length ?? 0;
    await list.loadMore();

    // 로드 중 이동하거나 목록이 더 이상 증가하지 않는 경우 — 어느 쪽이든 중단합니다.
    if (run !== docLoadRun || (list.topics?.length ?? 0) === before) {
      return;
    }
  }
}

// 문서 주제 목록을 A-Z로 정렬하며, 페이지의 <html lang>와 일치하도록 collation을 적용합니다.
let docSortObservers = new Map();

function docSortLocale() {
  const lang = document.documentElement.lang;
  if (!lang) {
    return undefined;
  }

  try {
    Intl.getCanonicalLocales(lang); // 잘못된 태그는 RangeError를 발생시킵니다
    return lang;
  } catch {
    return undefined; // 브라우저 기본 로케일로 폴백합니다
  }
}

function sortDocTopicList(body, locale) {
  const rows = [...body.querySelectorAll(":scope > .topic-list-item")];
  if (rows.length < 2) {
    return;
  }

  const sorted = [...rows].sort((a, b) => {
    const titleA = a.querySelector(".title")?.textContent.trim() ?? "";
    const titleB = b.querySelector(".title")?.textContent.trim() ?? "";
    return titleA.localeCompare(titleB, locale);
  });

  if (sorted.every((row, i) => rows[i] === row)) {
    return; // 이미 정렬됨 — 옵저버가 스스로를 다시 호출하는 것을 방지합니다
  }

// 행 뒤에 오는 요소 앞에 삽입하여, 로드 더 보기(load-more) 센티널을 마지막에 유지합니다.
  const anchor = rows[rows.length - 1].nextSibling;

// 첫 번째 보이는 행을 고정하여 그 위에 삽입될 때 스크롤이 점프하지 않도록 합니다.
  const pinned = rows.find((row) => row.getBoundingClientRect().bottom > 0);
  const pinnedTop = pinned?.getBoundingClientRect().top;

// 옵저버를 일시 중지하여 우리의 이동이 이 함수를 다시 진입하지 않도록 합니다.
  const observer = docSortObservers.get(body);
  observer?.disconnect();
  sorted.forEach((row) => body.insertBefore(row, anchor));
  observer?.observe(body, { childList: true });

  if (pinned) {
    window.scrollBy(0, pinned.getBoundingClientRect().top - pinnedTop);
  }
}

function sortDocCategoryTopicLists() {
  docSortObservers.forEach((observer) => observer.disconnect());
  docSortObservers = new Map();

  const locale = docSortLocale();

// flex/grid 컨테이너가 아니므로, 재배열은 DOM 노드를 이동해야 합니다 — CSS `order`는 무시됩니다.
  document.querySelectorAll(".topic-list.doc-simple-mode .topic-list-body").forEach((body) => {
    // 정렬 중 예외가 발생해도 목록이 항상 표시되도록 try/finally를 사용합니다.
    try {
      sortDocTopicList(body, locale);
    } finally {
      body.classList.add("docs-sorted");
    }

    // 로드 중 재배열은 로더를 멈추게 했으므로, 로딩이 안정된 후에만 정렬합니다.
    let settleTimer = null;
    const observer = new MutationObserver(() => {
      window.clearTimeout(settleTimer);
      settleTimer = window.setTimeout(() => {
        sortDocTopicList(body, locale);
      }, 400);
    });
    observer.observe(body, { childList: true });
    docSortObservers.set(body, observer);
  });
}

코드를 더 개선할 수 있을 것 같아, 제안이나 PR을 기꺼이 환영합니다.

음 - 안타깝게도 게시글의 링크를 클릭하면 404 오류가 발생합니다: https://github.com/somos-criptonautas/discourse-horizon-mod-nautas/blob/main/javascripts/api-initializers/init.gjs

아, 죄송합니다. URL을 업데이트했습니다. 이는 내부적인 내용이며 아직 개발 중이므로 실험적 단계로 봐 주시면 감사하겠습니다.

코드를 확인하시거나 사용해 보신 후 피드백, 제안, 또는 오류를 발견하시면 알려주세요! 가장 우아한 해결책은 아니지만, 저희 사용 사례에는 잘 작동합니다.