diff --git a/docs/content/features/search.ko.md b/docs/content/features/search.ko.md
@@ -58,6 +58,20 @@ enabled = false
색인 생성을 통째로 건너뜁니다.
+템플릿에는 같은 스위치가 `site.search`로 옵니다 — 현재 언어의 색인 URL이거나,
+꺼져 있으면 없습니다:
+
+```html
+{% if site.search %}
+<button id="search-trigger" data-index="{{ site.search }}">…</button>
+{% endif %}
+```
+
+`site.feed`와 같은 규칙입니다. 이 `if`는 방어가 아니라 검색을 끈 사이트에서도
+테마가 동작하게 하는 방법 그 자체입니다. 이 사이트의 검색 버튼과 팔레트, 푸터
+링크가 전부 이 뒤에 있습니다 — 그리고 URL은 템플릿이 `search-`에 언어 코드를
+이어 붙여 만드는 게 아니라 빌드가 줍니다.
+
## 순위
질의의 모든 단어가 그 행 어딘가에 나타나야 합니다. OR가 아니라 AND입니다. 제목에서
diff --git a/docs/content/features/search.md b/docs/content/features/search.md
@@ -61,6 +61,20 @@ enabled = false
skips the index entirely.
+Templates see the same switch as `site.search` — the current language's index
+URL, or nothing:
+
+```html
+{% if site.search %}
+<button id="search-trigger" data-index="{{ site.search }}">…</button>
+{% endif %}
+```
+
+The same rule as `site.feed`: the `if` is not defensive, it is how a theme
+works on sites that turned search off. This site's search button, the palette
+and the footer link all sit behind it — and the URL comes from the build, not
+from the template gluing `search-` to a language code.
+
## Ranking
Every term in the query must appear somewhere in a row — an AND, not an OR.
diff --git a/docs/content/templates/data.ko.md b/docs/content/templates/data.ko.md
@@ -17,6 +17,7 @@ toc = true
| `site.language` | 지금 렌더 중인 페이지의 언어. |
| `site.sections` | **이 언어의** 최상위 섹션들. |
| `site.highlight_css` | 생성된 하이라이트 스타일시트 URL. 강조가 꺼져 있으면 없습니다. |
+| `site.search` | 이 언어의 검색 색인 URL. `[search] enabled = false`면 없습니다. |
`site.sections`에는 현재 언어의 트리만 담깁니다. 내비게이션이 안전한 이유가
이것입니다. 번역되지 않은 페이지는 여기 없으므로 그 링크를 그릴 수가 없습니다.
diff --git a/docs/content/templates/data.md b/docs/content/templates/data.md
@@ -17,6 +17,7 @@ Two objects are in scope on every page: `site` and `page`.
| `site.language` | The language of the page being rendered. |
| `site.sections` | Top-level sections **in this language**. |
| `site.highlight_css` | URL of the generated highlight stylesheet, or nothing if highlighting is off. |
+| `site.search` | URL of this language's search index, or nothing with `[search] enabled = false`. |
`site.sections` contains only the current language's tree, which is what makes
navigation safe: an untranslated page is not in it, so a link to it cannot be
diff --git a/docs/templates/base.html b/docs/templates/base.html
@@ -78,12 +78,14 @@
{#- 크롬은 전부 같은 컨트롤이다. 하나만 텍스트 링크로 두면 그 하나가 고장난
것처럼 보인다. #}
<div class="tools">
+ {%- if site.search %}
<button id="search-trigger" class="chip search-trigger" type="button"
- data-index="{{ site.base_path }}/search-{{ page.language }}.json">
+ data-index="{{ site.search }}">
<span>{{ t("search") }}</span>
{#- 라벨은 search.js가 읽는 사람의 수정자 키로 바꿔 쓴다. 이건 JS가 없을 때 값. #}
<kbd>Ctrl /</kbd>
</button>
+ {%- endif %}
{%- for lang in page.translations %}
<a class="chip" href="{{ lang.url }}" hreflang="{{ lang.code }}">{{ lang.name }}</a>
{%- endfor %}
@@ -184,7 +186,9 @@
<li><a href="{{ site.base_path }}/sitemap.xml">sitemap.xml</a></li>
<li><a href="{{ site.base_path }}/robots.txt">robots.txt</a></li>
<li><a href="{{ site.base_path }}/llms.txt">llms.txt</a></li>
- <li><a href="{{ site.base_path }}/search-{{ page.language }}.json">{{ t("footer_search_index") }}</a></li>
+ {%- if site.search %}
+ <li><a href="{{ site.search }}">{{ t("footer_search_index") }}</a></li>
+ {%- endif %}
</ul>
</div>
</div>
@@ -192,6 +196,7 @@
<p class="colophon-note">{{ t("built_with") }}</p>
</footer>
+{%- if site.search %}
<dialog id="search-dialog" class="search-dialog" aria-label="{{ t("search_the_docs") }}">
<div class="search-panel">
<input type="search" autocomplete="off" spellcheck="false" aria-label="{{ t("search_the_docs") }}">
@@ -202,6 +207,7 @@
</div>
</div>
</dialog>
+{%- endif %}
<script>
document.querySelector(".theme-toggle").addEventListener("click", function () {
@@ -211,7 +217,9 @@ document.querySelector(".theme-toggle").addEventListener("click", function () {
try { localStorage.setItem("theme", next); } catch (e) {}
});
</script>
+{%- if site.search %}
<script src="{{ asset("js/search.js") }}" defer></script>
+{%- endif %}
<script src="{{ asset("js/ui.js") }}" defer
data-copy="{{ t("copy") }}" data-copied="{{ t("copied") }}"></script>
</body>
diff --git a/src/lib.rs b/src/lib.rs
@@ -445,6 +445,10 @@ fn render_page(
language: page.language.clone(),
sections,
feed: feed_url.map(str::to_string),
+ search: cfg
+ .search
+ .enabled
+ .then(|| format!("{}/{}", cfg.base_path(), search::path(&page.language))),
highlight_css: highlight_css.map(str::to_string),
};
@@ -544,6 +548,11 @@ fn render_standalone(
language: language.clone(),
sections,
feed: None,
+ // 404에도 검색은 준다 — 길 잃은 독자가 빠져나가는 길이다.
+ search: cfg
+ .search
+ .enabled
+ .then(|| format!("{}/{}", cfg.base_path(), search::path(&language))),
highlight_css: highlight_css.map(str::to_string),
};
@@ -690,6 +699,28 @@ mod search_toggle_tests {
let _ = std::fs::remove_dir_all(&dir);
}
+ #[test]
+ fn templates_see_the_index_url_or_nothing() {
+ // `site.search`는 `site.feed`와 같은 규칙이다 — 켜져 있으면 이 언어의
+ // 색인 URL, 꺼져 있으면 없음. 테마가 두 경우 모두에서 동작하는 근거다.
+ let tpl = "{% if site.search %}INDEX={{ site.search }}{% else %}NO-SEARCH{% endif %}";
+
+ let on = site_with("", "ctx-on");
+ std::fs::write(on.join("templates/page.html"), tpl).unwrap();
+ let html = build_to_memory(&opts(&on)).unwrap().files["index.html"].clone();
+ let html = String::from_utf8(html).unwrap();
+ assert!(html.contains("INDEX=/search-en.json"), "실제: {html}");
+
+ let off = site_with("\n[search]\nenabled = false\n", "ctx-off");
+ std::fs::write(off.join("templates/page.html"), tpl).unwrap();
+ let html = build_to_memory(&opts(&off)).unwrap().files["index.html"].clone();
+ let html = String::from_utf8(html).unwrap();
+ assert!(html.contains("NO-SEARCH"), "실제: {html}");
+
+ let _ = std::fs::remove_dir_all(&on);
+ let _ = std::fs::remove_dir_all(&off);
+ }
+
#[test]
fn a_static_search_index_wins_over_the_generated_one() {
let dir = site_with("", "static-wins");
diff --git a/src/render.rs b/src/render.rs
@@ -220,6 +220,11 @@ pub struct SiteCtx {
/// 이 언어의 Atom 피드 URL. 날짜 있는 페이지가 하나도 없으면 `None`이고,
/// 그때 템플릿은 자동 발견 링크를 넣지 않으면 된다.
pub feed: Option<String>,
+ /// 이 언어의 검색 색인 URL. `[search] enabled = false`면 `None`이고,
+ /// 그때 템플릿은 검색 UI를 넣지 않으면 된다 — `feed`와 같은 규칙이다.
+ /// URL 조립을 템플릿에 맡기지 않는 이유이기도 하다: 색인 파일명은 엔진
+ /// 지식이라, 템플릿이 손으로 만들면 끈 사이트에서 죽은 URL이 된다.
+ pub search: Option<String>,
/// 생성된 하이라이트 스타일시트 URL. 강조가 꺼져 있으면 `None`.
pub highlight_css: Option<String>,
}
|