(() => {
"use strict";
const categories = window.AKASHI_WIKI_CATEGORIES || [];
const docs = window.AKASHI_WIKI_DOCS || [];
if (!categories.length || !docs.length) {
throw new Error("Akashi wiki data is not loaded. Check docs-data.js script order.");
}
const els = {
body: document.body,
sidebar: document.getElementById("sidebar"),
menuButton: document.getElementById("menuButton"),
searchInput: document.getElementById("searchInput"),
categoryNav: document.getElementById("categoryNav"),
tagFilter: document.getElementById("tagFilter"),
docList: document.getElementById("docList"),
resultCount: document.getElementById("resultCount"),
activeFilter: document.getElementById("activeFilter"),
articleView: document.getElementById("articleView"),
currentPath: document.getElementById("currentPath"),
clearButton: document.getElementById("clearButton"),
copyLinkButton: document.getElementById("copyLinkButton"),
toast: document.getElementById("toast")
};
const state = {
query: "",
category: "all",
tag: null,
activeDocId: getDocIdFromHash() || "home"
};
const categoryLabel = (id) => categories.find((category) => category.id === id)?.label || id;
const normalize = (value) => value.toLocaleLowerCase("ko-KR").trim();
const uniqueTags = [...new Set(docs.flatMap((doc) => doc.tags))].sort((a, b) => a.localeCompare(b));
function getDocIdFromHash() {
const match = decodeURIComponent(window.location.hash).match(/^#\/doc\/([a-z0-9-]+)$/i);
return match ? match[1] : null;
}
function escapeHtml(value) {
return String(value)
.replaceAll("&", "&")
.replaceAll("<", "<")
.replaceAll(">", ">")
.replaceAll('"', """)
.replaceAll("'", "'");
}
function docSearchText(doc) {
const blockText = doc.blocks.map((block) => {
if (block.body) return block.body.join(" ");
if (block.items) return block.items.map((item) => typeof item === "string" ? item : Object.values(item).join(" ")).join(" ");
if (block.rows) return block.rows.flat().join(" ");
if (block.text) return block.text;
return "";
}).join(" ");
return normalize([doc.title, doc.summary, doc.category, doc.owner, doc.status, doc.tags.join(" "), blockText].join(" "));
}
function filteredDocs() {
const query = normalize(state.query);
return docs.filter((doc) => {
const categoryMatch = state.category === "all" || doc.category === state.category;
const tagMatch = !state.tag || doc.tags.includes(state.tag);
const queryMatch = !query || docSearchText(doc).includes(query);
return categoryMatch && tagMatch && queryMatch;
});
}
function renderCategories() {
els.categoryNav.innerHTML = categories.map((category) => {
const count = category.id === "all" ? docs.length : docs.filter((doc) => doc.category === category.id).length;
return `
`;
}).join("");
}
function renderTags() {
els.tagFilter.innerHTML = uniqueTags.map((tag) => `
`).join("");
}
function renderDocList() {
const list = filteredDocs();
if (!list.some((doc) => doc.id === state.activeDocId)) {
state.activeDocId = list[0]?.id || docs[0].id;
}
els.resultCount.textContent = `${list.length} docs`;
els.activeFilter.textContent = [categoryLabel(state.category), state.tag ? `#${state.tag}` : null].filter(Boolean).join(" · ");
if (!list.length) {
els.docList.innerHTML = `
검색 결과가 없습니다.
`;
return;
}
els.docList.innerHTML = list.map((doc) => `
`).join("");
}
function renderArticle() {
const doc = docs.find((item) => item.id === state.activeDocId) || docs[0];
const tags = doc.tags.map((tag) => `#${escapeHtml(tag)}`).join("");
els.currentPath.textContent = `${categoryLabel(doc.category)} / ${doc.title}`;
document.title = `${doc.title} | 아카시 LLM 위키`;
els.articleView.innerHTML = `
${escapeHtml(categoryLabel(doc.category))}
${escapeHtml(doc.title)}
${escapeHtml(doc.summary)}
${tags}
Owner ${escapeHtml(doc.owner)}
${escapeHtml(doc.status)}
${escapeHtml(doc.updated)}
${doc.blocks.map(renderBlock).join("")}
`;
}
function renderBlock(block) {
if (block.type === "section") {
const body = block.body.map((paragraph) => `${escapeHtml(paragraph)}
`).join("");
return `${escapeHtml(block.title)}
${body}`;
}
if (block.type === "callout") {
return `${escapeHtml(block.title)}${escapeHtml(block.text)}
`;
}
if (block.type === "code") {
return `${escapeHtml(block.text)}
`;
}
if (block.type === "checklist") {
return `${block.items.map((item) => `
✓${escapeHtml(item)}
`).join("")}
`;
}
if (block.type === "metrics") {
return `${block.items.map((item) => `
${escapeHtml(item.label)}${escapeHtml(item.value)}${escapeHtml(item.text)}
`).join("")}
`;
}
if (block.type === "flow") {
return `${block.items.map((item) => `
${escapeHtml(item.label)}${escapeHtml(item.title)}${escapeHtml(item.text)}
`).join("")}
`;
}
if (block.type === "matrix") {
return `${block.rows.map((row) => `
${escapeHtml(row[0])}${escapeHtml(row[1])}${escapeHtml(row[2])}
`).join("")}
`;
}
return "";
}
function renderAll() {
renderCategories();
renderTags();
renderDocList();
renderArticle();
}
function setActiveDoc(id, updateHash = true) {
if (!docs.some((doc) => doc.id === id)) return;
state.activeDocId = id;
if (updateHash) history.replaceState(null, "", `#/doc/${id}`);
renderDocList();
renderArticle();
els.articleView.focus({ preventScroll: true });
els.body.classList.remove("nav-open");
}
function showToast(message) {
els.toast.textContent = message;
els.toast.classList.add("is-visible");
window.clearTimeout(showToast.timer);
showToast.timer = window.setTimeout(() => els.toast.classList.remove("is-visible"), 1800);
}
els.categoryNav.addEventListener("click", (event) => {
const button = event.target.closest("[data-category]");
if (!button) return;
state.category = button.dataset.category;
renderAll();
});
els.tagFilter.addEventListener("click", (event) => {
const button = event.target.closest("[data-tag]");
if (!button) return;
state.tag = state.tag === button.dataset.tag ? null : button.dataset.tag;
renderAll();
});
els.docList.addEventListener("click", (event) => {
const button = event.target.closest("[data-doc-id]");
if (button) setActiveDoc(button.dataset.docId);
});
els.searchInput.addEventListener("input", (event) => {
state.query = event.target.value;
renderAll();
});
els.clearButton.addEventListener("click", () => {
state.query = "";
state.category = "all";
state.tag = null;
els.searchInput.value = "";
renderAll();
showToast("필터를 초기화했습니다.");
});
els.copyLinkButton.addEventListener("click", async () => {
const url = `${location.origin}${location.pathname}#/doc/${state.activeDocId}`;
try {
await navigator.clipboard.writeText(url);
showToast("문서 링크를 복사했습니다.");
} catch (error) {
showToast("현재 브라우저에서 복사를 지원하지 않습니다.");
}
});
els.menuButton.addEventListener("click", () => {
els.body.classList.toggle("nav-open");
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") els.body.classList.remove("nav-open");
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") {
event.preventDefault();
els.searchInput.focus();
els.body.classList.add("nav-open");
}
});
window.addEventListener("hashchange", () => {
const id = getDocIdFromHash();
if (id) setActiveDoc(id, false);
});
if (!getDocIdFromHash()) history.replaceState(null, "", "#/doc/home");
renderAll();
})();