graph functionality
This commit is contained in:
@@ -1014,3 +1014,37 @@ body.editor-mode .editor-metadata-row {
|
||||
fill: #1e2f6d;
|
||||
stroke: #1e2f6d;
|
||||
}
|
||||
|
||||
|
||||
/* 0.2.15 context navigation and sticky-anchor correction ---------------- */
|
||||
.context-link {
|
||||
color: var(--wiki-link);
|
||||
font-size: .76rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.context-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.markdown-body h1,
|
||||
.markdown-body h2,
|
||||
.markdown-body h3,
|
||||
.markdown-body h4,
|
||||
.markdown-body h5,
|
||||
.markdown-body h6 {
|
||||
scroll-margin-top: 82px;
|
||||
}
|
||||
|
||||
.wiki-graph-node-focus circle {
|
||||
r: 15px;
|
||||
fill: #1e2f6d;
|
||||
stroke: #1e2f6d;
|
||||
}
|
||||
|
||||
.wiki-graph-node-focus text {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@
|
||||
<input id="search-input" type="search" placeholder="Search wiki" data-tr-placeholder="search-wiki" autocomplete="off" aria-label="Search wiki">
|
||||
</form>
|
||||
<section class="sidebar-section toc-section">
|
||||
<div class="sidebar-heading" data-tr="contents">Contents</div>
|
||||
<div class="sidebar-heading"><span data-tr="contents">Contents</span> <a id="context-link" class="context-link hidden" href="#">(<span data-tr="context">context</span>)</a></div>
|
||||
<nav id="toc-list" class="toc-list"></nav>
|
||||
</section>
|
||||
<details class="sidebar-section pages-section">
|
||||
@@ -110,7 +110,7 @@
|
||||
|
||||
<section id="graph-view" class="hidden">
|
||||
<header class="page-header">
|
||||
<h1 data-tr="wiki-graph">Wiki graph</h1>
|
||||
<h1 id="graph-title" data-tr="wiki-graph">Wiki graph</h1>
|
||||
<div id="graph-summary" class="muted"></div>
|
||||
</header>
|
||||
<div class="wiki-graph-shell">
|
||||
|
||||
+38
-2
@@ -23,6 +23,7 @@
|
||||
$(id).classList.toggle("hidden", id !== viewId);
|
||||
}
|
||||
$("page-action-links").classList.toggle("hidden", viewId !== "page-view");
|
||||
$("context-link").classList.toggle("hidden", !(state.currentPage && ["page-view", "editor-view", "history-view"].includes(viewId)));
|
||||
document.body.classList.toggle("editor-mode", viewId === "editor-view");
|
||||
}
|
||||
|
||||
@@ -1182,7 +1183,7 @@
|
||||
return { nodes: state.pages, edges };
|
||||
}
|
||||
|
||||
function renderWikiGraph(data) {
|
||||
function renderWikiGraph(data, focusSlug = null) {
|
||||
const svg = $("wiki-graph");
|
||||
svg.replaceChildren();
|
||||
|
||||
@@ -1222,7 +1223,7 @@
|
||||
for (const page of nodes) {
|
||||
const position = positions.get(page.slug);
|
||||
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
||||
group.setAttribute("class", "wiki-graph-node");
|
||||
group.setAttribute("class", page.slug === focusSlug ? "wiki-graph-node wiki-graph-node-focus" : "wiki-graph-node");
|
||||
group.setAttribute("transform", `translate(${position.x} ${position.y})`);
|
||||
group.setAttribute("tabindex", "0");
|
||||
group.setAttribute("role", "link");
|
||||
@@ -1259,6 +1260,7 @@
|
||||
]);
|
||||
renderToc([], () => {});
|
||||
show("graph-view");
|
||||
$("graph-title").textContent = tr("wiki-graph", "Wiki graph");
|
||||
|
||||
const data = await loadGraphData();
|
||||
$("graph-summary").textContent = tr("graph-summary", "{pages} pages, {links} links")
|
||||
@@ -1267,6 +1269,39 @@
|
||||
renderWikiGraph(data);
|
||||
}
|
||||
|
||||
async function showContextGraph() {
|
||||
if (!state.currentPage) return;
|
||||
|
||||
const page = state.currentPage;
|
||||
state.previousView = "page-view";
|
||||
renderBreadcrumbs([
|
||||
{ label: state.siteTitle, href: "/" },
|
||||
{ label: page.title, href: `#/${encodeURIComponent(page.slug)}` },
|
||||
{ label: tr("context", "context") }
|
||||
]);
|
||||
renderToc([], () => {});
|
||||
show("graph-view");
|
||||
$("graph-title").textContent = tr("context-graph", "Context graph");
|
||||
|
||||
const data = await loadGraphData();
|
||||
const relatedSlugs = new Set([page.slug]);
|
||||
const contextEdges = [];
|
||||
|
||||
for (const edge of data.edges) {
|
||||
if (edge.from === page.slug || edge.to === page.slug) {
|
||||
relatedSlugs.add(edge.from);
|
||||
relatedSlugs.add(edge.to);
|
||||
contextEdges.push(edge);
|
||||
}
|
||||
}
|
||||
|
||||
const contextNodes = data.nodes.filter((node) => relatedSlugs.has(node.slug));
|
||||
$("graph-summary").textContent = tr("context-graph-summary", "{pages} related pages, {links} links")
|
||||
.replace("{pages}", String(Math.max(0, contextNodes.length - 1)))
|
||||
.replace("{links}", String(contextEdges.length));
|
||||
renderWikiGraph({ nodes: contextNodes, edges: contextEdges }, page.slug);
|
||||
}
|
||||
|
||||
function setConnectionStatus(status) {
|
||||
const node = $("connection-status");
|
||||
node.classList.remove("hidden", "online", "offline");
|
||||
@@ -1339,6 +1374,7 @@
|
||||
});
|
||||
$("todo-link").addEventListener("click", (event) => { event.preventDefault(); showTodos().catch((error) => console.error(error)); });
|
||||
$("graph-link").addEventListener("click", (event) => { event.preventDefault(); showGraph().catch((error) => console.error(error)); });
|
||||
$("context-link").addEventListener("click", (event) => { event.preventDefault(); showContextGraph().catch((error) => console.error(error)); });
|
||||
$("logout-link").addEventListener("click", async (event) => {
|
||||
event.preventDefault();
|
||||
await api("/api/logout", { method: "POST", body: "{}" });
|
||||
|
||||
Reference in New Issue
Block a user