1909 lines
58 KiB
HTML
1909 lines
58 KiB
HTML
<!doctype html>
|
|
<html lang="zh-Hans">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>llm-wiki 知识舆图 · 东方编辑部</title>
|
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='14' fill='%238B2E24'/%3E%3Cpath d='M10 43 C17 34 22 25 29 31 C35 18 42 14 54 43' fill='none' stroke='%23FFFDF7' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M14 49 C24 45 34 46 50 49' fill='none' stroke='%23FFFDF7' stroke-width='3' stroke-linecap='round' opacity='.75'/%3E%3Ccircle cx='43' cy='18' r='3' fill='%23FFFDF7'/%3E%3C/svg%3E">
|
|
<style>
|
|
:root {
|
|
color-scheme: light;
|
|
--bg: #f4efe4;
|
|
--surface: #fffdf7;
|
|
--surface-2: #f8f1e4;
|
|
--vellum: #e9ddc9;
|
|
--mist: #ece5d8;
|
|
--ink: #241f1a;
|
|
--muted: #6f6559;
|
|
--faint: #9b8f7e;
|
|
--rule: #d8cdbb;
|
|
--line: #cfc4b1;
|
|
--cinnabar: #8b2e24;
|
|
--cinnabar-2: #a23b2a;
|
|
--jade: #4b7564;
|
|
--green: #3e6b4b;
|
|
--night: #315f72;
|
|
--amber: #b7791f;
|
|
--violet: #6f557f;
|
|
--shadow: 0 18px 36px rgba(36, 31, 26, .11);
|
|
--soft-shadow: 0 10px 24px rgba(36, 31, 26, .08);
|
|
--radius: 12px;
|
|
--font-serif: "Noto Serif SC", "Songti SC", "STSong", Georgia, serif;
|
|
--font-ui: "Noto Sans SC", -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
--font-mono: "SFMono-Regular", ui-monospace, Menlo, Consolas, monospace;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
html,
|
|
body {
|
|
width: 100%;
|
|
min-height: 100%;
|
|
margin: 0;
|
|
background:
|
|
radial-gradient(circle at 18% 12%, rgba(255, 253, 247, .72), transparent 28%),
|
|
radial-gradient(circle at 90% 18%, rgba(75, 117, 100, .12), transparent 28%),
|
|
linear-gradient(145deg, #f4efe4 0%, #ece4d4 48%, #f6f0e4 100%);
|
|
color: var(--ink);
|
|
font-family: var(--font-ui);
|
|
overflow: hidden;
|
|
}
|
|
|
|
body::before,
|
|
body::after {
|
|
content: "";
|
|
position: fixed;
|
|
inset: 0;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
|
|
body::before {
|
|
opacity: .42;
|
|
background-image:
|
|
repeating-linear-gradient(0deg, transparent 0 31px, rgba(104, 88, 66, .055) 31px 32px),
|
|
repeating-linear-gradient(90deg, transparent 0 31px, rgba(104, 88, 66, .042) 31px 32px),
|
|
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.7' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .55 0 0 0 0 .48 0 0 0 0 .34 0 0 0 .18 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
|
|
mix-blend-mode: multiply;
|
|
}
|
|
|
|
body::after {
|
|
background:
|
|
linear-gradient(90deg, transparent 0 86px, rgba(139, 46, 36, .22) 86px 87px, transparent 87px),
|
|
linear-gradient(180deg, rgba(255, 255, 255, .28), transparent 22%);
|
|
}
|
|
|
|
button,
|
|
input,
|
|
a {
|
|
font: inherit;
|
|
}
|
|
|
|
button,
|
|
input,
|
|
a {
|
|
min-height: 44px;
|
|
}
|
|
|
|
a {
|
|
color: inherit;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
}
|
|
|
|
button:focus-visible,
|
|
input:focus-visible,
|
|
[tabindex]:focus-visible {
|
|
outline: 2px solid rgba(139, 46, 36, .72);
|
|
outline-offset: 3px;
|
|
}
|
|
|
|
.app {
|
|
position: relative;
|
|
z-index: 1;
|
|
height: 100vh;
|
|
display: grid;
|
|
grid-template-columns: minmax(236px, 276px) minmax(520px, 1fr) minmax(300px, 370px);
|
|
grid-template-rows: auto 1fr;
|
|
gap: 16px;
|
|
padding: 14px 16px 16px 24px;
|
|
}
|
|
|
|
.topbar {
|
|
grid-column: 1 / -1;
|
|
display: grid;
|
|
grid-template-columns: minmax(240px, 1fr) auto;
|
|
gap: 18px;
|
|
align-items: center;
|
|
padding: 14px 16px 13px 18px;
|
|
background: rgba(255, 253, 247, .82);
|
|
border: 1px solid rgba(216, 205, 187, .92);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--soft-shadow);
|
|
backdrop-filter: blur(8px);
|
|
}
|
|
|
|
.brand {
|
|
min-width: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
}
|
|
|
|
.seal {
|
|
width: 42px;
|
|
height: 42px;
|
|
display: grid;
|
|
place-items: center;
|
|
flex: 0 0 auto;
|
|
border-radius: 10px;
|
|
background:
|
|
linear-gradient(135deg, rgba(139, 46, 36, .95), rgba(162, 59, 42, .95)),
|
|
var(--cinnabar);
|
|
color: #fffdf7;
|
|
box-shadow: inset 0 0 0 1px rgba(255, 253, 247, .28), 0 10px 18px rgba(139, 46, 36, .22);
|
|
}
|
|
|
|
.seal svg {
|
|
width: 29px;
|
|
height: 29px;
|
|
display: block;
|
|
}
|
|
|
|
.brand h1 {
|
|
margin: 0;
|
|
font-family: var(--font-serif);
|
|
font-size: clamp(21px, 2vw, 31px);
|
|
line-height: 1.08;
|
|
letter-spacing: 0;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.brand p {
|
|
margin: 6px 0 0;
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
line-height: 1.4;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.status-strip {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.status-pill,
|
|
.chip {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 7px;
|
|
min-height: 32px;
|
|
padding: 6px 10px;
|
|
border-radius: 999px;
|
|
border: 1px solid rgba(216, 205, 187, .92);
|
|
background: rgba(248, 241, 228, .84);
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
button[disabled],
|
|
.is-disabled {
|
|
cursor: not-allowed;
|
|
opacity: .62;
|
|
filter: saturate(.65);
|
|
}
|
|
|
|
button[disabled],
|
|
.is-disabled,
|
|
.node.is-disabled {
|
|
border-style: dashed;
|
|
background-image: repeating-linear-gradient(135deg, transparent 0 8px, rgba(111, 101, 89, .08) 8px 10px);
|
|
}
|
|
|
|
.is-disabled::after {
|
|
content: "不可用";
|
|
display: inline-flex;
|
|
align-items: center;
|
|
min-height: 20px;
|
|
margin-left: 8px;
|
|
padding: 0 7px;
|
|
border-radius: 999px;
|
|
border: 1px solid rgba(111, 101, 89, .36);
|
|
color: var(--muted);
|
|
font-family: var(--font-ui);
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
letter-spacing: 0;
|
|
}
|
|
|
|
.status-dot {
|
|
width: 7px;
|
|
height: 7px;
|
|
border-radius: 99px;
|
|
background: var(--green);
|
|
box-shadow: 0 0 0 4px rgba(62, 107, 75, .12);
|
|
}
|
|
|
|
.icon-button,
|
|
.ghost-button,
|
|
.primary-button,
|
|
.state-button,
|
|
.nav-item,
|
|
.node {
|
|
border: 0;
|
|
cursor: pointer;
|
|
color: inherit;
|
|
}
|
|
|
|
.icon-button {
|
|
width: 44px;
|
|
height: 44px;
|
|
border-radius: 10px;
|
|
border: 1px solid rgba(216, 205, 187, .94);
|
|
background: rgba(255, 253, 247, .82);
|
|
display: inline-grid;
|
|
place-items: center;
|
|
color: var(--muted);
|
|
}
|
|
|
|
.ghost-button,
|
|
.primary-button,
|
|
.state-button {
|
|
border-radius: 10px;
|
|
padding: 0 12px;
|
|
border: 1px solid rgba(216, 205, 187, .94);
|
|
background: rgba(255, 253, 247, .82);
|
|
color: var(--ink);
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
justify-content: center;
|
|
}
|
|
|
|
.primary-button {
|
|
border-color: var(--cinnabar);
|
|
background: var(--cinnabar);
|
|
color: #fffdf7;
|
|
box-shadow: 0 10px 18px rgba(139, 46, 36, .18);
|
|
}
|
|
|
|
.ghost-button:hover,
|
|
.state-button:hover,
|
|
.icon-button:hover,
|
|
.nav-item:hover,
|
|
.node:hover {
|
|
transform: translateY(-1px);
|
|
border-color: rgba(139, 46, 36, .42);
|
|
box-shadow: 0 8px 16px rgba(36, 31, 26, .08);
|
|
}
|
|
|
|
.github-button {
|
|
font-weight: 700;
|
|
color: var(--ink);
|
|
}
|
|
|
|
.github-button svg {
|
|
width: 18px;
|
|
height: 18px;
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.mobile-atlas-preview,
|
|
.sidebar,
|
|
.drawer,
|
|
.canvas-card {
|
|
min-height: 0;
|
|
border: 1px solid rgba(216, 205, 187, .92);
|
|
background: rgba(255, 253, 247, .76);
|
|
border-radius: var(--radius);
|
|
box-shadow: var(--soft-shadow);
|
|
backdrop-filter: blur(9px);
|
|
}
|
|
|
|
.mobile-atlas-preview {
|
|
display: none;
|
|
}
|
|
|
|
.sidebar {
|
|
position: relative;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 14px;
|
|
padding: 15px 22px 15px 15px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.sidebar::after,
|
|
.drawer::before {
|
|
content: "";
|
|
position: absolute;
|
|
top: 16px;
|
|
bottom: 16px;
|
|
width: 1px;
|
|
background: rgba(139, 46, 36, .24);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.sidebar::after {
|
|
right: 14px;
|
|
}
|
|
|
|
.drawer::before {
|
|
left: 14px;
|
|
}
|
|
|
|
.section {
|
|
position: relative;
|
|
display: grid;
|
|
gap: 9px;
|
|
}
|
|
|
|
.section-title {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 8px;
|
|
min-height: 24px;
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
letter-spacing: .04em;
|
|
}
|
|
|
|
.section-title b {
|
|
color: var(--ink);
|
|
font-family: var(--font-serif);
|
|
font-size: 15px;
|
|
letter-spacing: 0;
|
|
}
|
|
|
|
.search-box {
|
|
position: relative;
|
|
}
|
|
|
|
.search-box input {
|
|
width: 100%;
|
|
min-height: 46px;
|
|
padding: 0 42px 0 13px;
|
|
border-radius: 10px;
|
|
border: 1px solid rgba(216, 205, 187, .95);
|
|
background: rgba(248, 241, 228, .78);
|
|
color: var(--ink);
|
|
}
|
|
|
|
.search-box span {
|
|
position: absolute;
|
|
right: 13px;
|
|
top: 50%;
|
|
translate: 0 -50%;
|
|
color: var(--faint);
|
|
}
|
|
|
|
.nav-list,
|
|
.queue-list,
|
|
.start-list,
|
|
.neighbor-list,
|
|
.notes-list {
|
|
display: grid;
|
|
gap: 8px;
|
|
}
|
|
|
|
.nav-item,
|
|
.queue-card,
|
|
.start-card,
|
|
.neighbor-card,
|
|
.note-card,
|
|
.state-panel {
|
|
width: 100%;
|
|
min-width: 0;
|
|
padding: 10px 12px;
|
|
border-radius: 10px;
|
|
border: 1px solid rgba(216, 205, 187, .92);
|
|
background: rgba(248, 241, 228, .66);
|
|
text-align: left;
|
|
}
|
|
|
|
.nav-item {
|
|
display: grid;
|
|
grid-template-columns: 10px 1fr auto;
|
|
align-items: center;
|
|
gap: 9px;
|
|
}
|
|
|
|
.nav-item[aria-pressed="true"] {
|
|
border-color: rgba(139, 46, 36, .46);
|
|
background: rgba(255, 253, 247, .94);
|
|
box-shadow: inset 3px 0 0 var(--cinnabar), 0 7px 16px rgba(36, 31, 26, .07);
|
|
}
|
|
|
|
.swatch {
|
|
width: 10px;
|
|
height: 28px;
|
|
border-radius: 99px;
|
|
background: var(--night);
|
|
}
|
|
|
|
.nav-copy,
|
|
.card-copy {
|
|
min-width: 0;
|
|
}
|
|
|
|
.nav-copy strong,
|
|
.card-copy strong {
|
|
display: block;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-family: var(--font-serif);
|
|
font-size: 14px;
|
|
line-height: 1.3;
|
|
}
|
|
|
|
.nav-copy span,
|
|
.card-copy span {
|
|
display: block;
|
|
margin-top: 3px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.count {
|
|
font-family: var(--font-mono);
|
|
color: var(--cinnabar);
|
|
font-size: 12px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.segmented {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
gap: 6px;
|
|
padding: 5px;
|
|
border-radius: 12px;
|
|
background: rgba(233, 221, 201, .52);
|
|
border: 1px solid rgba(216, 205, 187, .82);
|
|
}
|
|
|
|
.segmented button {
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
border-radius: 8px;
|
|
border: 0;
|
|
background: transparent;
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.segmented button[aria-pressed="true"] {
|
|
color: var(--ink);
|
|
background: rgba(255, 253, 247, .92);
|
|
box-shadow: 0 5px 12px rgba(36, 31, 26, .08);
|
|
}
|
|
|
|
.queue-card {
|
|
display: grid;
|
|
gap: 10px;
|
|
}
|
|
|
|
.queue-metrics {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px;
|
|
}
|
|
|
|
.metric {
|
|
padding: 9px;
|
|
border-radius: 9px;
|
|
background: rgba(255, 253, 247, .72);
|
|
border: 1px dashed rgba(216, 205, 187, .95);
|
|
}
|
|
|
|
.metric span {
|
|
display: block;
|
|
color: var(--muted);
|
|
font-size: 11px;
|
|
}
|
|
|
|
.metric b {
|
|
display: block;
|
|
margin-top: 5px;
|
|
font-family: var(--font-serif);
|
|
font-size: 18px;
|
|
line-height: 1;
|
|
}
|
|
|
|
.canvas-card {
|
|
position: relative;
|
|
overflow: hidden;
|
|
display: grid;
|
|
grid-template-rows: auto 1fr auto;
|
|
background:
|
|
linear-gradient(180deg, rgba(255, 253, 247, .62), rgba(248, 241, 228, .58)),
|
|
var(--surface-2);
|
|
}
|
|
|
|
.canvas-card::before,
|
|
.canvas-card::after {
|
|
content: "";
|
|
position: absolute;
|
|
pointer-events: none;
|
|
z-index: 0;
|
|
}
|
|
|
|
.canvas-card::before {
|
|
inset: 64px 16px 72px;
|
|
opacity: .66;
|
|
background:
|
|
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='920' height='620' viewBox='0 0 920 620'%3E%3Cg fill='none' stroke='%23cfc4b1' stroke-width='1.35' opacity='.9'%3E%3Cpath d='M-20 480 C88 420 140 448 212 386 C298 312 364 378 454 322 C548 264 608 278 704 228 C798 180 856 206 946 150'/%3E%3Cpath d='M-28 525 C86 462 166 492 244 426 C332 350 394 414 482 352 C574 288 638 306 734 254 C820 208 880 236 952 196'/%3E%3Cpath d='M18 372 C112 324 170 340 240 282 C324 212 386 272 466 222 C542 174 636 190 700 142 C782 82 854 120 930 78'/%3E%3Cpath d='M44 540 C118 520 148 558 218 522 C306 478 340 514 430 462 C514 414 586 430 674 386 C778 334 826 362 912 322'/%3E%3Cpath d='M-32 262 C60 224 112 242 174 198 C244 148 300 184 374 142 C450 96 520 106 594 72 C686 32 760 56 930 4'/%3E%3Cpath d='M72 470 C156 430 218 456 290 404 C366 350 424 392 512 338 C602 284 662 302 742 260 C812 224 856 238 936 198'/%3E%3Cpath d='M80 302 C154 266 218 286 286 238 C350 190 422 218 492 178 C568 134 650 142 724 96 C796 52 854 72 930 34'/%3E%3C/g%3E%3Cg fill='none' stroke='%23315f72' stroke-width='1.1' opacity='.22'%3E%3Cpath d='M120 154 L206 198 L316 162 L430 236 L546 176 L678 226 L798 144'/%3E%3Cpath d='M172 398 L260 338 L368 390 L486 316 L604 356 L732 294'/%3E%3Cpath d='M236 112 L330 92 L426 140 L528 106 L632 156 L738 112'/%3E%3C/g%3E%3Cg fill='%23315f72' opacity='.18'%3E%3Ccircle cx='120' cy='154' r='3'/%3E%3Ccircle cx='316' cy='162' r='2.6'/%3E%3Ccircle cx='546' cy='176' r='2.8'/%3E%3Ccircle cx='798' cy='144' r='3'/%3E%3Ccircle cx='260' cy='338' r='2.4'/%3E%3Ccircle cx='604' cy='356' r='2.4'/%3E%3Ccircle cx='738' cy='112' r='2.8'/%3E%3C/g%3E%3C/svg%3E") center / cover no-repeat;
|
|
}
|
|
|
|
.canvas-card::after {
|
|
inset: 0;
|
|
background:
|
|
radial-gradient(circle at 52% 38%, rgba(49, 95, 114, .16), transparent 18%),
|
|
radial-gradient(circle at 70% 56%, rgba(75, 117, 100, .12), transparent 22%),
|
|
radial-gradient(circle at 35% 62%, rgba(139, 46, 36, .10), transparent 18%),
|
|
linear-gradient(90deg, rgba(255, 253, 247, .1), rgba(236, 229, 216, .18), rgba(255, 253, 247, .1));
|
|
mix-blend-mode: multiply;
|
|
}
|
|
|
|
.mobile-atlas-preview {
|
|
position: relative;
|
|
overflow: hidden;
|
|
padding: 12px;
|
|
background:
|
|
linear-gradient(180deg, rgba(255, 253, 247, .76), rgba(248, 241, 228, .66)),
|
|
var(--surface-2);
|
|
}
|
|
|
|
.mobile-atlas-preview::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 8px 10px;
|
|
pointer-events: none;
|
|
opacity: .72;
|
|
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='340' height='112' viewBox='0 0 340 112'%3E%3Cg fill='none' stroke='%23cfc4b1' stroke-width='1.1' opacity='.86'%3E%3Cpath d='M-4 88 C32 58 64 76 96 50 C132 22 164 44 202 30 C246 14 280 34 346 10'/%3E%3Cpath d='M-10 106 C34 78 72 96 108 68 C148 38 178 60 218 44 C258 28 296 46 348 24'/%3E%3Cpath d='M10 66 C48 42 78 52 112 30 C150 8 184 24 218 16 C258 6 292 16 340 2'/%3E%3C/g%3E%3Cg fill='none' stroke='%23315f72' stroke-width='1' opacity='.26'%3E%3Cpath d='M48 68 L104 44 L164 56 L226 32 L292 50'/%3E%3Cpath d='M84 88 L146 74 L206 90 L272 70'/%3E%3C/g%3E%3Cg fill='%23315f72' opacity='.32'%3E%3Ccircle cx='48' cy='68' r='3'/%3E%3Ccircle cx='164' cy='56' r='3'/%3E%3Ccircle cx='292' cy='50' r='3'/%3E%3C/g%3E%3Cg fill='%238b2e24' opacity='.8'%3E%3Ccircle cx='104' cy='44' r='4'/%3E%3C/g%3E%3Cg fill='%234b7564' opacity='.72'%3E%3Ccircle cx='226' cy='32' r='4'/%3E%3C/g%3E%3C/svg%3E") center / cover no-repeat;
|
|
}
|
|
|
|
.preview-copy,
|
|
.preview-map {
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
.preview-copy {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.preview-copy strong {
|
|
font-family: var(--font-serif);
|
|
font-size: 16px;
|
|
}
|
|
|
|
.preview-copy span {
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.preview-map {
|
|
height: 94px;
|
|
border-radius: 11px;
|
|
border: 1px solid rgba(216, 205, 187, .86);
|
|
background: rgba(255, 253, 247, .38);
|
|
}
|
|
|
|
.canvas-toolbar,
|
|
.canvas-footer {
|
|
position: relative;
|
|
z-index: 2;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
padding: 12px 14px;
|
|
}
|
|
|
|
.canvas-toolbar {
|
|
border-bottom: 1px solid rgba(216, 205, 187, .78);
|
|
background: rgba(255, 253, 247, .72);
|
|
}
|
|
|
|
.canvas-title {
|
|
min-width: 0;
|
|
}
|
|
|
|
.canvas-title strong {
|
|
display: block;
|
|
font-family: var(--font-serif);
|
|
font-size: 18px;
|
|
line-height: 1.2;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.canvas-title span {
|
|
display: block;
|
|
margin-top: 4px;
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.canvas-actions {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.atlas {
|
|
position: relative;
|
|
z-index: 1;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.mist {
|
|
position: absolute;
|
|
inset: auto -10% 8% -10%;
|
|
height: 28%;
|
|
pointer-events: none;
|
|
background:
|
|
radial-gradient(ellipse at 28% 55%, rgba(255, 253, 247, .56), transparent 56%),
|
|
radial-gradient(ellipse at 70% 48%, rgba(236, 229, 216, .6), transparent 58%);
|
|
filter: blur(18px);
|
|
opacity: .9;
|
|
animation: drift 12s ease-in-out infinite alternate;
|
|
}
|
|
|
|
.graph-svg {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: visible;
|
|
}
|
|
|
|
.edge {
|
|
fill: none;
|
|
stroke-linecap: round;
|
|
opacity: .74;
|
|
}
|
|
|
|
.edge.extracted {
|
|
stroke: rgba(49, 95, 114, .74);
|
|
stroke-width: 2.4;
|
|
}
|
|
|
|
.edge.inferred {
|
|
stroke: rgba(75, 117, 100, .62);
|
|
stroke-width: 1.7;
|
|
stroke-dasharray: 6 8;
|
|
}
|
|
|
|
.edge.ambiguous {
|
|
stroke: rgba(183, 121, 31, .66);
|
|
stroke-width: 1.6;
|
|
stroke-dasharray: 2 7;
|
|
}
|
|
|
|
.edge.unverified {
|
|
stroke: rgba(111, 101, 89, .45);
|
|
stroke-width: 1.2;
|
|
stroke-dasharray: 1 8;
|
|
}
|
|
|
|
.node {
|
|
position: absolute;
|
|
z-index: 3;
|
|
min-height: 46px;
|
|
max-width: 178px;
|
|
padding: 8px 11px;
|
|
border-radius: 12px;
|
|
border: 1px solid rgba(216, 205, 187, .98);
|
|
background: rgba(255, 253, 247, .88);
|
|
box-shadow: 0 12px 22px rgba(36, 31, 26, .09), inset 0 0 0 1px rgba(255, 255, 255, .44);
|
|
translate: -50% -50%;
|
|
transition: opacity .16s ease, transform .16s ease, box-shadow .16s ease, border-color .16s ease;
|
|
text-align: left;
|
|
}
|
|
|
|
.node::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: -7px;
|
|
border-radius: 17px;
|
|
background: radial-gradient(circle, rgba(49, 95, 114, .18), transparent 66%);
|
|
z-index: -1;
|
|
opacity: .46;
|
|
}
|
|
|
|
.node[data-type="topic"] {
|
|
border-left: 5px solid var(--cinnabar);
|
|
}
|
|
|
|
.node[data-type="entity"] {
|
|
border-left: 5px solid var(--night);
|
|
}
|
|
|
|
.node[data-type="source"] {
|
|
border-left: 5px solid var(--jade);
|
|
}
|
|
|
|
.node[aria-pressed="true"] {
|
|
border-color: rgba(139, 46, 36, .74);
|
|
box-shadow: 0 16px 28px rgba(139, 46, 36, .16), 0 0 0 4px rgba(139, 46, 36, .1);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.node.is-dim,
|
|
.edge.is-dim {
|
|
opacity: .18;
|
|
}
|
|
|
|
.node.is-disabled {
|
|
opacity: .72;
|
|
}
|
|
|
|
.node.is-disabled::before {
|
|
background: radial-gradient(circle, rgba(111, 101, 89, .16), transparent 66%);
|
|
}
|
|
|
|
.node.is-disabled .spark {
|
|
background: var(--muted);
|
|
box-shadow: none;
|
|
}
|
|
|
|
.node.is-hidden {
|
|
display: none;
|
|
}
|
|
|
|
.node-kind {
|
|
display: block;
|
|
color: var(--muted);
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.node-name {
|
|
display: block;
|
|
max-width: 146px;
|
|
margin-top: 3px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-family: var(--font-serif);
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
line-height: 1.25;
|
|
}
|
|
|
|
.node-meta {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
margin-top: 6px;
|
|
color: var(--faint);
|
|
font-size: 11px;
|
|
}
|
|
|
|
.spark {
|
|
width: 5px;
|
|
height: 5px;
|
|
border-radius: 99px;
|
|
background: var(--night);
|
|
box-shadow: 0 0 10px rgba(49, 95, 114, .7);
|
|
}
|
|
|
|
.empty-state,
|
|
.loading-state,
|
|
.error-state {
|
|
position: absolute;
|
|
z-index: 5;
|
|
left: 50%;
|
|
top: 50%;
|
|
width: min(360px, calc(100% - 48px));
|
|
translate: -50% -50%;
|
|
padding: 18px;
|
|
border-radius: 14px;
|
|
border: 1px solid rgba(216, 205, 187, .95);
|
|
background: rgba(255, 253, 247, .9);
|
|
box-shadow: var(--shadow);
|
|
text-align: center;
|
|
display: none;
|
|
}
|
|
|
|
.state-title {
|
|
margin: 0;
|
|
font-family: var(--font-serif);
|
|
font-size: 18px;
|
|
}
|
|
|
|
.state-copy {
|
|
margin: 8px 0 0;
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
line-height: 1.55;
|
|
}
|
|
|
|
.spinner {
|
|
width: 34px;
|
|
height: 34px;
|
|
margin: 0 auto 12px;
|
|
border-radius: 99px;
|
|
border: 2px solid rgba(216, 205, 187, .95);
|
|
border-top-color: var(--cinnabar);
|
|
animation: spin .9s linear infinite;
|
|
}
|
|
|
|
.atlas[data-mode="loading"] .loading-state,
|
|
.atlas[data-mode="empty"] .empty-state,
|
|
.atlas[data-mode="error"] .error-state {
|
|
display: block;
|
|
}
|
|
|
|
.atlas[data-mode="loading"] .node,
|
|
.atlas[data-mode="loading"] .edge,
|
|
.atlas[data-mode="empty"] .node,
|
|
.atlas[data-mode="empty"] .edge,
|
|
.atlas[data-mode="error"] .node,
|
|
.atlas[data-mode="error"] .edge {
|
|
opacity: .1;
|
|
}
|
|
|
|
.canvas-footer {
|
|
border-top: 1px solid rgba(216, 205, 187, .78);
|
|
background: rgba(255, 253, 247, .7);
|
|
align-items: stretch;
|
|
}
|
|
|
|
.legend,
|
|
.mini-map,
|
|
.insight {
|
|
min-width: 0;
|
|
border: 1px solid rgba(216, 205, 187, .82);
|
|
border-radius: 10px;
|
|
background: rgba(248, 241, 228, .62);
|
|
padding: 10px;
|
|
}
|
|
|
|
.legend {
|
|
flex: 1.2;
|
|
}
|
|
|
|
.mini-map {
|
|
flex: .9;
|
|
min-width: 138px;
|
|
}
|
|
|
|
.insight {
|
|
flex: 1.15;
|
|
}
|
|
|
|
.footer-label {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px;
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
}
|
|
|
|
.confidence-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
margin-top: 8px;
|
|
}
|
|
|
|
.confidence {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
color: var(--muted);
|
|
}
|
|
|
|
.confidence i {
|
|
width: 22px;
|
|
border-top: 2px solid var(--night);
|
|
}
|
|
|
|
.confidence:nth-child(2) i {
|
|
border-top-color: var(--jade);
|
|
border-top-style: dashed;
|
|
}
|
|
|
|
.confidence:nth-child(3) i {
|
|
border-top-color: var(--amber);
|
|
border-top-style: dotted;
|
|
}
|
|
|
|
.confidence:nth-child(4) i {
|
|
border-top-color: var(--muted);
|
|
border-top-style: dotted;
|
|
}
|
|
|
|
.mini-map svg {
|
|
width: 100%;
|
|
height: 54px;
|
|
display: block;
|
|
}
|
|
|
|
.insight strong {
|
|
display: block;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-family: var(--font-serif);
|
|
font-size: 13px;
|
|
}
|
|
|
|
.insight p {
|
|
margin: 5px 0 0;
|
|
color: var(--muted);
|
|
font-size: 12px;
|
|
line-height: 1.45;
|
|
}
|
|
|
|
.drawer {
|
|
position: relative;
|
|
min-width: 0;
|
|
display: grid;
|
|
grid-template-rows: auto 1fr auto;
|
|
padding: 0 0 0 12px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.drawer-header {
|
|
padding: 18px 18px 13px;
|
|
border-bottom: 1px solid rgba(216, 205, 187, .82);
|
|
background: rgba(255, 253, 247, .72);
|
|
}
|
|
|
|
.eyebrow {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
color: var(--cinnabar);
|
|
font-family: var(--font-mono);
|
|
font-size: 11px;
|
|
letter-spacing: .04em;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.drawer h2 {
|
|
margin: 9px 0 0;
|
|
font-family: var(--font-serif);
|
|
font-size: 25px;
|
|
line-height: 1.16;
|
|
letter-spacing: 0;
|
|
}
|
|
|
|
.drawer-subtitle {
|
|
margin: 8px 0 0;
|
|
color: var(--muted);
|
|
line-height: 1.55;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.drawer-body {
|
|
min-height: 0;
|
|
padding: 16px 18px;
|
|
overflow-y: auto;
|
|
display: grid;
|
|
align-content: start;
|
|
gap: 15px;
|
|
}
|
|
|
|
.meta-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px;
|
|
}
|
|
|
|
.meta-box {
|
|
min-width: 0;
|
|
padding: 10px;
|
|
border: 1px solid rgba(216, 205, 187, .9);
|
|
border-radius: 10px;
|
|
background: rgba(248, 241, 228, .62);
|
|
}
|
|
|
|
.meta-box span {
|
|
display: block;
|
|
color: var(--muted);
|
|
font-family: var(--font-mono);
|
|
font-size: 10px;
|
|
letter-spacing: .04em;
|
|
}
|
|
|
|
.meta-box b {
|
|
display: block;
|
|
margin-top: 5px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
font-family: var(--font-serif);
|
|
font-size: 14px;
|
|
}
|
|
|
|
.drawer-section {
|
|
display: grid;
|
|
gap: 8px;
|
|
}
|
|
|
|
.drawer-section h3 {
|
|
margin: 0;
|
|
color: var(--ink);
|
|
font-family: var(--font-serif);
|
|
font-size: 15px;
|
|
}
|
|
|
|
.summary-card {
|
|
padding: 12px 13px;
|
|
border-left: 3px solid var(--cinnabar);
|
|
border-radius: 0 10px 10px 0;
|
|
background: rgba(248, 241, 228, .66);
|
|
color: var(--ink);
|
|
line-height: 1.68;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.knowledge-card {
|
|
display: grid;
|
|
gap: 10px;
|
|
padding: 13px 14px;
|
|
border-radius: 10px;
|
|
border: 1px solid rgba(216, 205, 187, .88);
|
|
background: rgba(255, 253, 247, .7);
|
|
color: var(--ink);
|
|
line-height: 1.72;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.knowledge-card p {
|
|
margin: 0;
|
|
}
|
|
|
|
.neighbor-section {
|
|
padding: 0;
|
|
border: 1px solid rgba(216, 205, 187, .88);
|
|
border-radius: 10px;
|
|
background: rgba(248, 241, 228, .48);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.neighbor-section > summary {
|
|
min-height: 44px;
|
|
padding: 0 12px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 10px;
|
|
cursor: pointer;
|
|
list-style: none;
|
|
color: var(--ink);
|
|
font-family: var(--font-serif);
|
|
font-size: 15px;
|
|
}
|
|
|
|
.neighbor-section > summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
.neighbor-section > summary span {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.neighbor-section > summary small {
|
|
color: var(--muted);
|
|
font-family: var(--font-ui);
|
|
font-size: 12px;
|
|
font-weight: 400;
|
|
}
|
|
|
|
.neighbor-section > summary::after {
|
|
content: "展开";
|
|
flex: 0 0 auto;
|
|
color: var(--cinnabar);
|
|
font-family: var(--font-ui);
|
|
font-size: 12px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.neighbor-section[open] > summary::after {
|
|
content: "收起";
|
|
}
|
|
|
|
.neighbor-section .neighbor-list {
|
|
padding: 0 12px 12px;
|
|
}
|
|
|
|
.neighbor-section[open] .neighbor-list {
|
|
max-height: 168px;
|
|
overflow-y: auto;
|
|
padding-right: 8px;
|
|
scrollbar-gutter: stable;
|
|
}
|
|
|
|
.neighbor-section[open] .neighbor-list::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
.neighbor-section[open] .neighbor-list::-webkit-scrollbar-thumb {
|
|
border-radius: 999px;
|
|
background: rgba(111, 101, 89, .32);
|
|
}
|
|
|
|
.neighbor-card,
|
|
.note-card,
|
|
.start-card {
|
|
display: grid;
|
|
gap: 4px;
|
|
color: var(--ink);
|
|
}
|
|
|
|
.note-card {
|
|
border-style: dashed;
|
|
background: rgba(255, 253, 247, .62);
|
|
color: var(--muted);
|
|
font-size: 13px;
|
|
line-height: 1.55;
|
|
}
|
|
|
|
.drawer-actions {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 9px;
|
|
padding: 13px 18px 16px;
|
|
border-top: 1px solid rgba(216, 205, 187, .82);
|
|
background: rgba(255, 253, 247, .76);
|
|
}
|
|
|
|
.state-dock {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 7px;
|
|
}
|
|
|
|
.state-button {
|
|
min-width: 44px;
|
|
min-height: 44px;
|
|
padding: 0 12px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.state-button[aria-pressed="true"] {
|
|
border-color: rgba(139, 46, 36, .55);
|
|
color: var(--cinnabar);
|
|
background: rgba(255, 253, 247, .96);
|
|
}
|
|
|
|
.hidden-count {
|
|
display: none;
|
|
margin-top: 8px;
|
|
padding: 10px 12px;
|
|
border-radius: 10px;
|
|
border: 1px solid rgba(183, 121, 31, .48);
|
|
background: rgba(248, 232, 196, .55);
|
|
color: var(--ink);
|
|
font-size: 12px;
|
|
line-height: 1.5;
|
|
}
|
|
|
|
.hidden-count.is-visible {
|
|
display: block;
|
|
}
|
|
|
|
@keyframes drift {
|
|
from {
|
|
transform: translateX(-2%);
|
|
}
|
|
to {
|
|
transform: translateX(3%);
|
|
}
|
|
}
|
|
|
|
@keyframes spin {
|
|
to {
|
|
rotate: 360deg;
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: .001ms !important;
|
|
animation-iteration-count: 1 !important;
|
|
scroll-behavior: auto !important;
|
|
transition-duration: .001ms !important;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 1180px) {
|
|
.app {
|
|
grid-template-columns: 238px minmax(460px, 1fr) 318px;
|
|
gap: 12px;
|
|
padding: 12px;
|
|
}
|
|
|
|
.status-strip .status-pill:nth-child(3) {
|
|
display: none;
|
|
}
|
|
|
|
.canvas-footer {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
}
|
|
|
|
.insight {
|
|
grid-column: 1 / -1;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
body {
|
|
overflow: auto;
|
|
}
|
|
|
|
.app {
|
|
min-height: 100vh;
|
|
height: auto;
|
|
grid-template-columns: 1fr;
|
|
grid-template-rows: auto auto auto minmax(620px, 72vh) auto;
|
|
padding: 10px;
|
|
}
|
|
|
|
.mobile-atlas-preview {
|
|
display: block;
|
|
}
|
|
|
|
.topbar {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.status-strip {
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
.sidebar {
|
|
max-height: none;
|
|
overflow: visible;
|
|
}
|
|
|
|
.drawer {
|
|
min-height: 560px;
|
|
}
|
|
|
|
.brand h1,
|
|
.brand p {
|
|
white-space: normal;
|
|
}
|
|
|
|
.canvas-footer {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 620px) {
|
|
.brand {
|
|
align-items: flex-start;
|
|
}
|
|
|
|
.seal {
|
|
width: 38px;
|
|
height: 38px;
|
|
}
|
|
|
|
.canvas-toolbar,
|
|
.canvas-actions,
|
|
.drawer-actions {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
.status-pill,
|
|
.chip {
|
|
min-height: 44px;
|
|
}
|
|
|
|
.state-dock,
|
|
.segmented {
|
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
}
|
|
|
|
.state-dock {
|
|
display: grid;
|
|
}
|
|
|
|
.node {
|
|
max-width: 144px;
|
|
min-width: 44px;
|
|
padding: 7px 9px;
|
|
}
|
|
|
|
.node-name {
|
|
max-width: 116px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main class="app">
|
|
<header class="topbar" aria-label="图谱标题与状态">
|
|
<div class="brand">
|
|
<div class="seal" aria-hidden="true">
|
|
<svg viewBox="0 0 48 48" role="img">
|
|
<path d="M5 32c7-6 10-16 17-10 5-10 12-13 21 10" fill="none" stroke="currentColor" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
<path d="M8 38c8-3 18-2 32 0" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" opacity=".76"></path>
|
|
<path d="M13 28c5 2 10 2 15 0" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" opacity=".52"></path>
|
|
<circle cx="35" cy="11" r="2.8" fill="currentColor"></circle>
|
|
<circle cx="40" cy="18" r="1.6" fill="currentColor" opacity=".8"></circle>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<h1>llm-wiki 知识舆图</h1>
|
|
<p>国风知识库·数字山水图</p>
|
|
</div>
|
|
</div>
|
|
<div class="status-strip">
|
|
<span class="status-pill"><i class="status-dot"></i>已生成 128 个索引点</span>
|
|
<span class="status-pill">关系 246 条</span>
|
|
<span class="status-pill">最近更新 2026-04-27</span>
|
|
<button class="ghost-button github-button" type="button" aria-label="打开 GitHub 项目">
|
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"></path>
|
|
<path d="M9 18c-4.51 2-5-2-7-2"></path>
|
|
</svg>
|
|
<span>GitHub</span>
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="mobile-atlas-preview" aria-label="移动端图谱预览">
|
|
<div class="preview-copy">
|
|
<strong>数字山水图谱</strong>
|
|
<span>128 点 · 246 关系</span>
|
|
</div>
|
|
<div class="preview-map" aria-hidden="true"></div>
|
|
</section>
|
|
|
|
<aside class="sidebar" aria-label="文献索引导航">
|
|
<section class="section">
|
|
<div class="section-title"><b>文献索引</b></div>
|
|
<div class="search-box">
|
|
<input id="search" type="search" autocomplete="off" placeholder="搜索节点、来源或主题">
|
|
<span aria-hidden="true">⌕</span>
|
|
</div>
|
|
<div id="no-results" class="hidden-count">没有匹配结果,清除筛选后可回到完整图谱。</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<div class="section-title"><b>社区</b></div>
|
|
<div class="nav-list" id="community-list"></div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<div class="section-title"><b>聚焦</b></div>
|
|
<div class="segmented" aria-label="聚焦模式">
|
|
<button type="button" data-focus="all" aria-pressed="true">全图</button>
|
|
<button type="button" data-focus="core" aria-pressed="false">核心</button>
|
|
<button type="button" data-focus="source" aria-pressed="false">来源</button>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<div class="section-title"><b>学习队列</b></div>
|
|
<div class="queue-card">
|
|
<div class="queue-metrics">
|
|
<div class="metric"><span>今日待读</span><b>5</b></div>
|
|
<div class="metric"><span>已批注</span><b>17</b></div>
|
|
</div>
|
|
<div class="queue-list">
|
|
<span class="chip">上下文工程</span>
|
|
<span class="chip">知识编译</span>
|
|
<span class="chip">来源置信度</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="section">
|
|
<div class="section-title"><b>推荐起点</b></div>
|
|
<div class="start-list" id="start-list"></div>
|
|
</section>
|
|
</aside>
|
|
|
|
<section class="canvas-card" aria-label="知识图谱画布">
|
|
<div class="canvas-toolbar">
|
|
<div class="canvas-title">
|
|
<strong id="canvas-title">知识地图 · 全局视图</strong>
|
|
<span id="canvas-subtitle">社区地貌、关系路径与批注状态保持同步</span>
|
|
</div>
|
|
<div class="canvas-actions">
|
|
<button class="ghost-button" type="button" id="fit-view">回到全图</button>
|
|
<button class="ghost-button" type="button" id="toggle-dim">弱化未选中</button>
|
|
<div class="state-dock" aria-label="数据状态">
|
|
<button class="state-button" type="button" data-mode="normal" aria-pressed="true">正常</button>
|
|
<button class="state-button" type="button" data-mode="loading" aria-pressed="false">加载</button>
|
|
<button class="state-button" type="button" data-mode="empty" aria-pressed="false">空</button>
|
|
<button class="state-button" type="button" data-mode="error" aria-pressed="false">错误</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="atlas" id="atlas" data-mode="normal">
|
|
<svg class="graph-svg" viewBox="0 0 1000 680" preserveAspectRatio="none" aria-hidden="true">
|
|
<defs>
|
|
<filter id="softGlow">
|
|
<feGaussianBlur stdDeviation="5" result="blur"></feGaussianBlur>
|
|
<feMerge>
|
|
<feMergeNode in="blur"></feMergeNode>
|
|
<feMergeNode in="SourceGraphic"></feMergeNode>
|
|
</feMerge>
|
|
</filter>
|
|
</defs>
|
|
<g id="edge-layer"></g>
|
|
</svg>
|
|
<div class="mist" aria-hidden="true"></div>
|
|
<div id="node-layer"></div>
|
|
<div class="loading-state" role="status" aria-live="polite">
|
|
<div class="spinner" aria-hidden="true"></div>
|
|
<p class="state-title">正在铺开知识地图</p>
|
|
<p class="state-copy">图谱数据已读取,正在整理社区、关系和标签。</p>
|
|
</div>
|
|
<div class="empty-state" role="status" aria-live="polite">
|
|
<p class="state-title">这座知识库还没有图谱</p>
|
|
<p class="state-copy">消化素材后会自动生成节点、来源和关系。</p>
|
|
</div>
|
|
<div class="error-state" role="alert">
|
|
<p class="state-title">图谱数据没有加载成功</p>
|
|
<p class="state-copy">请确认本地 graph-data.json 存在且格式完整。</p>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="canvas-footer" aria-label="图谱二级信息">
|
|
<div class="legend">
|
|
<div class="footer-label"><span>关系置信度</span><span>说明</span></div>
|
|
<div class="confidence-list">
|
|
<span class="confidence"><i></i>直接提取</span>
|
|
<span class="confidence"><i></i>推断关联</span>
|
|
<span class="confidence"><i></i>存在歧义</span>
|
|
<span class="confidence"><i></i>未核实</span>
|
|
</div>
|
|
</div>
|
|
<div class="mini-map">
|
|
<div class="footer-label"><span>小地图</span><span>MAP</span></div>
|
|
<svg viewBox="0 0 160 54" aria-hidden="true">
|
|
<path d="M8 40 C34 20 54 36 76 22 C98 8 118 24 150 12" fill="none" stroke="#cfc4b1" stroke-width="1.4"></path>
|
|
<circle cx="28" cy="34" r="3" fill="#315f72"></circle>
|
|
<circle cx="58" cy="27" r="3" fill="#8b2e24"></circle>
|
|
<circle cx="90" cy="21" r="3" fill="#4b7564"></circle>
|
|
<circle cx="124" cy="18" r="3" fill="#b7791f"></circle>
|
|
<rect x="22" y="12" width="76" height="32" rx="5" fill="none" stroke="#8b2e24" stroke-width="1.2"></rect>
|
|
</svg>
|
|
</div>
|
|
<div class="insight">
|
|
<div class="footer-label"><span>洞察</span><span>INSIGHT</span></div>
|
|
<strong id="insight-title">来源节点连接度偏高</strong>
|
|
<p id="insight-copy">建议从“知识编译”进入,可同时看到方法论、缓存与图谱生成的关系。</p>
|
|
</div>
|
|
</footer>
|
|
</section>
|
|
|
|
<aside class="drawer" aria-label="批注札记详情">
|
|
<header class="drawer-header">
|
|
<div class="eyebrow" id="drawer-kind"><span class="spark"></span>TOPIC · SELECTED</div>
|
|
<h2 id="drawer-title">知识编译</h2>
|
|
<p class="drawer-subtitle" id="drawer-subtitle">方法论 · EXTRACTED · wiki/topics/知识编译.md</p>
|
|
</header>
|
|
|
|
<div class="drawer-body">
|
|
<section class="drawer-section">
|
|
<h3>摘要</h3>
|
|
<div class="summary-card" id="drawer-summary">知识编译强调“整理一次,持续维护”。它连接素材消化、结构化页面、缓存、查询和图谱展示,是 llm-wiki 区别于临时问答的关键。</div>
|
|
</section>
|
|
|
|
<section class="drawer-section">
|
|
<h3>知识内容</h3>
|
|
<div class="knowledge-card" id="drawer-content">
|
|
<p>知识编译把素材、主题、实体和来源串成一张可维护的地图。它不是临时总结,而是让后续查询、图谱和复盘都能复用同一份整理结果。</p>
|
|
<p>在图谱中,这个节点连接素材消化、缓存、查询和图谱展示,适合作为理解 llm-wiki 工作方式的入口。</p>
|
|
</div>
|
|
</section>
|
|
|
|
<details class="drawer-section neighbor-section" id="neighbor-details">
|
|
<summary><span>相邻节点 <small id="drawer-neighbor-count">5 个</small></span></summary>
|
|
<div class="neighbor-list" id="neighbor-list"></div>
|
|
</details>
|
|
|
|
<section class="drawer-section">
|
|
<h3>札记</h3>
|
|
<div class="notes-list">
|
|
<div class="note-card">批注:适合放在 README 首屏,作为“不是一次性总结”的视觉证明。</div>
|
|
<div class="note-card">待看:对比“学习驾驶舱”和“图谱入口”的职责边界。</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
|
|
<footer class="drawer-actions">
|
|
<button class="primary-button" type="button">加入学习队列</button>
|
|
<button class="ghost-button" type="button">查看来源</button>
|
|
</footer>
|
|
</aside>
|
|
</main>
|
|
|
|
<script>
|
|
const communities = [
|
|
{ id: "all", label: "全部社区", meta: "128 个节点", count: "ALL", color: "#8b2e24" },
|
|
{ id: "method", label: "方法论", meta: "知识编译、查询、沉淀", count: 36, color: "#315f72" },
|
|
{ id: "runtime", label: "图谱运行时", meta: "模板、状态、交互", count: 42, color: "#4b7564" },
|
|
{ id: "sources", label: "素材来源", meta: "网页、PDF、公众号、YouTube", count: 28, color: "#b7791f" },
|
|
{ id: "quality", label: "质量检查", meta: "lint、缓存、隐私自查", count: 22, color: "#6f557f" }
|
|
];
|
|
|
|
const nodes = [
|
|
{
|
|
id: "compile", label: "知识编译", type: "topic", community: "method", x: 50, y: 46, weight: 96,
|
|
source: "wiki/topics/知识编译.md", confidence: "EXTRACTED",
|
|
summary: "知识编译强调“整理一次,持续维护”。它连接素材消化、结构化页面、缓存、查询和图谱展示,是 llm-wiki 区别于临时问答的关键。"
|
|
},
|
|
{
|
|
id: "ingest", label: "素材消化", type: "topic", community: "method", x: 31, y: 35, weight: 86,
|
|
source: "SKILL.md", confidence: "EXTRACTED",
|
|
summary: "把链接、文件或粘贴内容转成结构化 wiki 页面,并保留来源、摘要、实体和主题关系。"
|
|
},
|
|
{
|
|
id: "query", label: "查询沉淀", type: "topic", community: "method", x: 68, y: 37, weight: 78,
|
|
source: "wiki/queries/", confidence: "INFERRED",
|
|
summary: "查询不是一次性回答,有价值的综合结论可以保存回知识库,让后续上下文继续生长。"
|
|
},
|
|
{
|
|
id: "graph", label: "交互式图谱", type: "entity", community: "runtime", x: 52, y: 66, weight: 91,
|
|
source: "templates/graph-styles/wash/", confidence: "EXTRACTED",
|
|
summary: "离线 HTML 图谱承载搜索、筛选、社区聚类、洞察、小地图和学习驾驶舱。"
|
|
},
|
|
{
|
|
id: "visible", label: "可见状态快照", type: "entity", community: "runtime", x: 72, y: 63, weight: 72,
|
|
source: "docs/solutions/best-practices/learning-cockpit-visible-state-reframe-2026-04-24.md", confidence: "EXTRACTED",
|
|
summary: "社区、聚焦、搜索和边过滤统一收敛到一份可见状态,避免各面板显示不一致。"
|
|
},
|
|
{
|
|
id: "cache", label: "智能缓存", type: "entity", community: "quality", x: 26, y: 64, weight: 68,
|
|
source: "docs/solutions/workflow-issues/cache-update-reliability-2026-04-16.md", confidence: "EXTRACTED",
|
|
summary: "写入即更新,避免素材重复整理,也让弱模型不容易漏掉缓存。"
|
|
},
|
|
{
|
|
id: "web", label: "网页文章", type: "source", community: "sources", x: 16, y: 24, weight: 54,
|
|
source: "raw/articles/", confidence: "EXTRACTED",
|
|
summary: "网页内容可通过可选提取器进入主线;失败时保留手动回退入口。"
|
|
},
|
|
{
|
|
id: "pdf", label: "PDF 与本地文件", type: "source", community: "sources", x: 18, y: 78, weight: 48,
|
|
source: "raw/pdfs/", confidence: "EXTRACTED",
|
|
summary: "本地文件属于核心主线,不依赖外挂,适合长期研究资料积累。"
|
|
},
|
|
{
|
|
id: "wechat", label: "微信公众号", type: "source", community: "sources", x: 82, y: 22, weight: 44,
|
|
source: "raw/wechat/", confidence: "AMBIGUOUS", unavailable: true,
|
|
summary: "公众号提取依赖可选环境,当前提取器不可用时应显示明确状态和手动粘贴入口。"
|
|
},
|
|
{
|
|
id: "lint", label: "健康检查", type: "entity", community: "quality", x: 84, y: 79, weight: 61,
|
|
source: "scripts/lint-wiki.sh", confidence: "INFERRED",
|
|
summary: "检测孤立页面、断链、索引一致性和潜在矛盾,帮助知识库保持可维护。"
|
|
}
|
|
];
|
|
|
|
const edges = [
|
|
["compile", "ingest", "extracted"],
|
|
["compile", "query", "extracted"],
|
|
["compile", "graph", "extracted"],
|
|
["graph", "visible", "extracted"],
|
|
["ingest", "cache", "inferred"],
|
|
["ingest", "web", "extracted"],
|
|
["ingest", "pdf", "extracted"],
|
|
["ingest", "wechat", "ambiguous"],
|
|
["query", "lint", "inferred"],
|
|
["visible", "lint", "unverified"],
|
|
["cache", "graph", "inferred"],
|
|
["web", "compile", "ambiguous"]
|
|
];
|
|
|
|
const communityList = document.getElementById("community-list");
|
|
const startList = document.getElementById("start-list");
|
|
const nodeLayer = document.getElementById("node-layer");
|
|
const edgeLayer = document.getElementById("edge-layer");
|
|
const search = document.getElementById("search");
|
|
const noResults = document.getElementById("no-results");
|
|
const atlas = document.getElementById("atlas");
|
|
const canvasTitle = document.getElementById("canvas-title");
|
|
const canvasSubtitle = document.getElementById("canvas-subtitle");
|
|
const insightTitle = document.getElementById("insight-title");
|
|
const insightCopy = document.getElementById("insight-copy");
|
|
|
|
let selectedNodeId = "compile";
|
|
let activeCommunity = "all";
|
|
let focusMode = "all";
|
|
let dimUnselected = false;
|
|
|
|
const nodeById = new Map(nodes.map(node => [node.id, node]));
|
|
|
|
function nodeKind(node) {
|
|
if (node.type === "topic") return "TOPIC";
|
|
if (node.type === "source") return "SOURCE";
|
|
return "ENTITY";
|
|
}
|
|
|
|
function confidenceLabel(confidence) {
|
|
const labels = {
|
|
EXTRACTED: "直接提取",
|
|
INFERRED: "推断关联",
|
|
AMBIGUOUS: "存在歧义",
|
|
UNVERIFIED: "未核实"
|
|
};
|
|
return labels[confidence] || confidence;
|
|
}
|
|
|
|
function communityLabel(id) {
|
|
return (communities.find(item => item.id === id) || {}).label || "未分组";
|
|
}
|
|
|
|
function buildCommunities() {
|
|
communityList.innerHTML = "";
|
|
communities.forEach(item => {
|
|
const button = document.createElement("button");
|
|
button.className = "nav-item";
|
|
button.type = "button";
|
|
button.dataset.community = item.id;
|
|
button.setAttribute("aria-pressed", item.id === activeCommunity ? "true" : "false");
|
|
button.innerHTML = `
|
|
<i class="swatch" style="background:${item.color}"></i>
|
|
<span class="nav-copy"><strong>${item.label}</strong><span>${item.meta}</span></span>
|
|
<span class="count">${item.count}</span>
|
|
`;
|
|
button.addEventListener("click", () => {
|
|
activeCommunity = item.id;
|
|
applyFilters();
|
|
});
|
|
communityList.appendChild(button);
|
|
});
|
|
}
|
|
|
|
function buildStarts() {
|
|
startList.innerHTML = "";
|
|
["compile", "graph", "visible"].forEach(id => {
|
|
const node = nodeById.get(id);
|
|
const button = document.createElement("button");
|
|
button.className = "start-card";
|
|
button.type = "button";
|
|
button.innerHTML = `<span class="card-copy"><strong>${node.label}</strong><span>${communityLabel(node.community)} · ${confidenceLabel(node.confidence)}</span></span>`;
|
|
button.addEventListener("click", () => selectNode(id));
|
|
startList.appendChild(button);
|
|
});
|
|
}
|
|
|
|
function makePath(a, b) {
|
|
const x1 = a.x * 10;
|
|
const y1 = a.y * 6.8;
|
|
const x2 = b.x * 10;
|
|
const y2 = b.y * 6.8;
|
|
const mx = (x1 + x2) / 2;
|
|
const my = (y1 + y2) / 2;
|
|
const curve = Math.max(-70, Math.min(70, (a.y - b.y) * 1.7));
|
|
return `M ${x1} ${y1} Q ${mx + curve} ${my - 22} ${x2} ${y2}`;
|
|
}
|
|
|
|
function buildGraph() {
|
|
edgeLayer.innerHTML = "";
|
|
edges.forEach(([from, to, type], index) => {
|
|
const a = nodeById.get(from);
|
|
const b = nodeById.get(to);
|
|
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
path.setAttribute("d", makePath(a, b));
|
|
path.setAttribute("class", `edge ${type}`);
|
|
path.dataset.from = from;
|
|
path.dataset.to = to;
|
|
path.dataset.edgeId = `edge-${index}`;
|
|
edgeLayer.appendChild(path);
|
|
});
|
|
|
|
nodeLayer.innerHTML = "";
|
|
nodes.forEach(node => {
|
|
const button = document.createElement("button");
|
|
button.className = `node${node.unavailable ? " is-disabled" : ""}`;
|
|
button.type = "button";
|
|
button.dataset.id = node.id;
|
|
button.dataset.type = node.type;
|
|
button.dataset.community = node.community;
|
|
button.style.left = `${node.x}%`;
|
|
button.style.top = `${node.y}%`;
|
|
button.setAttribute("aria-pressed", node.id === selectedNodeId ? "true" : "false");
|
|
button.innerHTML = `
|
|
<span class="node-kind">${nodeKind(node)}</span>
|
|
<span class="node-name">${node.label}</span>
|
|
<span class="node-meta"><i class="spark"></i>${node.unavailable ? "来源暂不可用" : node.weight}</span>
|
|
`;
|
|
button.addEventListener("click", () => selectNode(node.id));
|
|
button.addEventListener("mouseenter", () => highlightNeighborhood(node.id));
|
|
button.addEventListener("mouseleave", () => applyFilters());
|
|
nodeLayer.appendChild(button);
|
|
});
|
|
}
|
|
|
|
function connectedIds(id) {
|
|
const ids = new Set([id]);
|
|
edges.forEach(([from, to]) => {
|
|
if (from === id) ids.add(to);
|
|
if (to === id) ids.add(from);
|
|
});
|
|
return ids;
|
|
}
|
|
|
|
function visibleNodes() {
|
|
const q = search.value.trim().toLowerCase();
|
|
return nodes.filter(node => {
|
|
const communityOk = activeCommunity === "all" || node.community === activeCommunity;
|
|
const focusOk =
|
|
focusMode === "all" ||
|
|
(focusMode === "core" && node.weight >= 68) ||
|
|
(focusMode === "source" && node.type === "source");
|
|
const searchOk = !q || `${node.label} ${node.source} ${node.summary}`.toLowerCase().includes(q);
|
|
return communityOk && focusOk && searchOk;
|
|
});
|
|
}
|
|
|
|
function applyFilters() {
|
|
buildCommunities();
|
|
const visible = visibleNodes();
|
|
const visibleIds = new Set(visible.map(node => node.id));
|
|
const hasNoResults = visible.length === 0 && atlas.dataset.mode === "normal";
|
|
|
|
document.querySelectorAll(".node").forEach(el => {
|
|
const isVisible = visibleIds.has(el.dataset.id);
|
|
el.classList.toggle("is-hidden", !isVisible);
|
|
const shouldDim = dimUnselected && selectedNodeId && el.dataset.id !== selectedNodeId && !connectedIds(selectedNodeId).has(el.dataset.id);
|
|
el.classList.toggle("is-dim", isVisible && shouldDim);
|
|
el.setAttribute("aria-pressed", el.dataset.id === selectedNodeId ? "true" : "false");
|
|
});
|
|
|
|
document.querySelectorAll(".edge").forEach(edge => {
|
|
const edgeVisible = visibleIds.has(edge.dataset.from) && visibleIds.has(edge.dataset.to);
|
|
edge.classList.toggle("is-dim", !edgeVisible || (dimUnselected && selectedNodeId && ![edge.dataset.from, edge.dataset.to].includes(selectedNodeId)));
|
|
});
|
|
|
|
noResults.classList.toggle("is-visible", hasNoResults);
|
|
const communityName = activeCommunity === "all" ? "全局视图" : communityLabel(activeCommunity);
|
|
canvasTitle.textContent = `知识地图 · ${communityName}`;
|
|
canvasSubtitle.textContent = hasNoResults ? "当前筛选没有匹配节点" : `${visible.length} 个节点在当前范围内可见`;
|
|
insightTitle.textContent = hasNoResults ? "没有匹配结果" : "来源节点连接度偏高";
|
|
insightCopy.textContent = hasNoResults ? "可以清除搜索词或切回全图查看完整关系。" : "建议从“知识编译”进入,可同时看到方法论、缓存与图谱生成的关系。";
|
|
}
|
|
|
|
function highlightNeighborhood(id) {
|
|
if (atlas.dataset.mode !== "normal") return;
|
|
const ids = connectedIds(id);
|
|
document.querySelectorAll(".node").forEach(el => {
|
|
el.classList.toggle("is-dim", !ids.has(el.dataset.id));
|
|
});
|
|
document.querySelectorAll(".edge").forEach(edge => {
|
|
edge.classList.toggle("is-dim", edge.dataset.from !== id && edge.dataset.to !== id);
|
|
});
|
|
}
|
|
|
|
function renderKnowledgeContent(node, neighbors) {
|
|
const content = document.getElementById("drawer-content");
|
|
const paragraphs = [
|
|
`${node.label}属于「${communityLabel(node.community)}」脉络。${node.summary}`,
|
|
neighbors.length
|
|
? `它当前连接到 ${neighbors.map(item => `「${item.label}」`).join("、")}。这些关系说明它在图谱中既是阅读入口,也是后续追踪来源和主题的线索。`
|
|
: "这个节点暂时没有相邻节点,适合作为独立资料页继续补充来源和批注。",
|
|
node.type === "source"
|
|
? "作为来源节点,它更适合承载出处、摘录和可靠性判断;相关主题会在图谱中向外展开。"
|
|
: "作为主题或实体节点,它更适合承载概念解释、关联判断和学习笔记;来源证据会在相邻节点中展开。"
|
|
];
|
|
|
|
content.innerHTML = "";
|
|
paragraphs.forEach(text => {
|
|
const p = document.createElement("p");
|
|
p.textContent = text;
|
|
content.appendChild(p);
|
|
});
|
|
}
|
|
|
|
function selectNode(id) {
|
|
selectedNodeId = id;
|
|
const node = nodeById.get(id);
|
|
const neighbors = [...connectedIds(id)].filter(item => item !== id).map(item => nodeById.get(item));
|
|
|
|
document.getElementById("drawer-kind").innerHTML = `<span class="spark"></span>${nodeKind(node)} · ${node.unavailable ? "UNAVAILABLE" : "SELECTED"}`;
|
|
document.getElementById("drawer-title").textContent = node.label;
|
|
document.getElementById("drawer-subtitle").textContent = `${communityLabel(node.community)} · ${confidenceLabel(node.confidence)} · ${node.source}`;
|
|
document.getElementById("drawer-neighbor-count").textContent = `${neighbors.length} 个`;
|
|
document.getElementById("drawer-summary").textContent = node.summary;
|
|
renderKnowledgeContent(node, neighbors);
|
|
|
|
const list = document.getElementById("neighbor-list");
|
|
list.innerHTML = "";
|
|
neighbors.forEach(neighbor => {
|
|
const button = document.createElement("button");
|
|
button.className = "neighbor-card";
|
|
button.type = "button";
|
|
button.innerHTML = `<span class="card-copy"><strong>${neighbor.label}</strong><span>${nodeKind(neighbor)} · ${communityLabel(neighbor.community)}</span></span>`;
|
|
button.addEventListener("click", () => selectNode(neighbor.id));
|
|
list.appendChild(button);
|
|
});
|
|
|
|
applyFilters();
|
|
}
|
|
|
|
document.querySelectorAll("[data-focus]").forEach(button => {
|
|
button.addEventListener("click", () => {
|
|
focusMode = button.dataset.focus;
|
|
document.querySelectorAll("[data-focus]").forEach(item => item.setAttribute("aria-pressed", item === button ? "true" : "false"));
|
|
applyFilters();
|
|
});
|
|
});
|
|
|
|
document.querySelectorAll("[data-mode]").forEach(button => {
|
|
button.addEventListener("click", () => {
|
|
const mode = button.dataset.mode;
|
|
atlas.dataset.mode = mode;
|
|
document.querySelectorAll("[data-mode]").forEach(item => item.setAttribute("aria-pressed", item === button ? "true" : "false"));
|
|
applyFilters();
|
|
});
|
|
});
|
|
|
|
document.getElementById("toggle-dim").addEventListener("click", () => {
|
|
dimUnselected = !dimUnselected;
|
|
document.getElementById("toggle-dim").textContent = dimUnselected ? "显示全部层级" : "弱化未选中";
|
|
applyFilters();
|
|
});
|
|
|
|
document.getElementById("fit-view").addEventListener("click", () => {
|
|
activeCommunity = "all";
|
|
focusMode = "all";
|
|
search.value = "";
|
|
document.querySelectorAll("[data-focus]").forEach(item => item.setAttribute("aria-pressed", item.dataset.focus === "all" ? "true" : "false"));
|
|
applyFilters();
|
|
selectNode("compile");
|
|
});
|
|
|
|
search.addEventListener("input", applyFilters);
|
|
|
|
buildCommunities();
|
|
buildStarts();
|
|
buildGraph();
|
|
selectNode(selectedNodeId);
|
|
</script>
|
|
</body>
|
|
</html>
|