web更新
parent
b7d4cc8782
commit
f857a90977
|
|
@ -20,6 +20,7 @@ from meeting_memory.meeting_processor import meeting_processor, state_store
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
STATIC_DIR = Path(__file__).resolve().parent / "static"
|
STATIC_DIR = Path(__file__).resolve().parent / "static"
|
||||||
|
STATIC_V2_DIR = Path(__file__).resolve().parent / "static_v2"
|
||||||
RAW_DIR = Path(config.storage.raw_dir)
|
RAW_DIR = Path(config.storage.raw_dir)
|
||||||
IMPORT_JOBS = {}
|
IMPORT_JOBS = {}
|
||||||
IMPORT_JOBS_LOCK = threading.Lock()
|
IMPORT_JOBS_LOCK = threading.Lock()
|
||||||
|
|
@ -29,8 +30,22 @@ class GraphDemoHandler(SimpleHTTPRequestHandler):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, directory=str(STATIC_DIR), **kwargs)
|
super().__init__(*args, directory=str(STATIC_DIR), **kwargs)
|
||||||
|
|
||||||
|
# ── Route: serve /static_v2/* from the v2 directory ──
|
||||||
|
def translate_path(self, path):
|
||||||
|
parsed = urlparse(path)
|
||||||
|
raw = parsed.path
|
||||||
|
|
||||||
|
# Serve /static_v2/* from static_v2 directory
|
||||||
|
if raw.startswith("/static_v2/"):
|
||||||
|
rel = raw[len("/static_v2/"):]
|
||||||
|
return str(STATIC_V2_DIR / rel)
|
||||||
|
|
||||||
|
return super().translate_path(path)
|
||||||
|
|
||||||
def do_GET(self):
|
def do_GET(self):
|
||||||
parsed = urlparse(self.path)
|
parsed = urlparse(self.path)
|
||||||
|
|
||||||
|
# API endpoints
|
||||||
if parsed.path == "/api/dashboard":
|
if parsed.path == "/api/dashboard":
|
||||||
self._handle_dashboard()
|
self._handle_dashboard()
|
||||||
return
|
return
|
||||||
|
|
@ -55,10 +70,16 @@ class GraphDemoHandler(SimpleHTTPRequestHandler):
|
||||||
if parsed.path == "/api/import-status":
|
if parsed.path == "/api/import-status":
|
||||||
self._handle_import_status(parsed.query)
|
self._handle_import_status(parsed.query)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Page routing — serve v2 HTML as default
|
||||||
if parsed.path in ("/", "/index.html"):
|
if parsed.path in ("/", "/index.html"):
|
||||||
self.path = "/index.html"
|
self.path = "/static_v2/index.html"
|
||||||
elif parsed.path == "/graph":
|
elif parsed.path == "/graph":
|
||||||
self.path = "/graph.html"
|
self.path = "/static_v2/graph.html"
|
||||||
|
elif parsed.path == "/graph.html":
|
||||||
|
self.path = "/static_v2/graph.html"
|
||||||
|
|
||||||
|
# JS files (/app.js, /graph.js) resolve to STATIC_DIR via default translate_path
|
||||||
super().do_GET()
|
super().do_GET()
|
||||||
|
|
||||||
def do_POST(self):
|
def do_POST(self):
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,7 @@ const graphNodeLimit = document.getElementById("graphNodeLimit");
|
||||||
const graphEdgeLimit = document.getElementById("graphEdgeLimit");
|
const graphEdgeLimit = document.getElementById("graphEdgeLimit");
|
||||||
const graphSvg = document.getElementById("graphSvg");
|
const graphSvg = document.getElementById("graphSvg");
|
||||||
const graphMeta = document.getElementById("graphMeta");
|
const graphMeta = document.getElementById("graphMeta");
|
||||||
const graphDetail = document.getElementById("graphDetail");
|
const detailPanel = document.getElementById("detailPanel");
|
||||||
const relatedSearch = document.getElementById("relatedSearch");
|
|
||||||
const graphTypeFilter = document.getElementById("graphTypeFilter");
|
const graphTypeFilter = document.getElementById("graphTypeFilter");
|
||||||
|
|
||||||
let selectedEntityTypes = null;
|
let selectedEntityTypes = null;
|
||||||
|
|
@ -76,35 +75,35 @@ async function loadGraphKinds() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderInspector(content) {
|
function renderInspector(content) {
|
||||||
graphDetail.innerHTML = content;
|
detailPanel.innerHTML = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadRelated(query) {
|
async function loadRelated(query) {
|
||||||
if (!query) {
|
if (!query) return;
|
||||||
relatedSearch.innerHTML = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}&limit=4`);
|
const response = await fetch(`/api/search?q=${encodeURIComponent(query)}&limit=4`);
|
||||||
const payload = await response.json();
|
const payload = await response.json();
|
||||||
const results = payload.results || [];
|
const results = payload.results || [];
|
||||||
if (!results.length) {
|
if (!results.length) {
|
||||||
relatedSearch.innerHTML = empty("没有更多相关检索结果");
|
detailPanel.insertAdjacentHTML("beforeend", `
|
||||||
|
<div class="detail-section">
|
||||||
|
<p class="eyebrow">Related</p>
|
||||||
|
<div class="empty-state">没有更多相关检索结果</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
relatedSearch.innerHTML = `
|
detailPanel.insertAdjacentHTML("beforeend", `
|
||||||
<div class="panel-head">
|
<div class="detail-section">
|
||||||
<div>
|
|
||||||
<p class="eyebrow">Related</p>
|
<p class="eyebrow">Related</p>
|
||||||
<h3>相关检索</h3>
|
<h3>相关检索</h3>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
${results.map((item) => `
|
${results.map((item) => `
|
||||||
<article class="result-card">
|
<article class="result-card">
|
||||||
<strong>${h(item.title || item.kind || "结果")}</strong>
|
<strong>${h(item.title || item.kind || "结果")}</strong>
|
||||||
<p>${h(item.text || "")}</p>
|
<p>${h(item.text || "")}</p>
|
||||||
</article>
|
</article>
|
||||||
`).join("")}
|
`).join("")}
|
||||||
`;
|
</div>
|
||||||
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderGraph(payload) {
|
function renderGraph(payload) {
|
||||||
|
|
@ -125,7 +124,6 @@ function renderGraph(payload) {
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
graphSvg.innerHTML = "";
|
graphSvg.innerHTML = "";
|
||||||
renderInspector(empty("当前没有可显示的图谱数据"));
|
renderInspector(empty("当前没有可显示的图谱数据"));
|
||||||
relatedSearch.innerHTML = "";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -409,19 +407,29 @@ function renderGraph(payload) {
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
renderInspector(`
|
renderInspector(`
|
||||||
<div class="detail-card">
|
<div class="detail-section">
|
||||||
<p class="eyebrow">${h(node.kind)}</p>
|
<p class="eyebrow">${h(node.kind)}</p>
|
||||||
<h3>${h(node.label)}</h3>
|
<h3>${h(node.label)}</h3>
|
||||||
${body}
|
${body}
|
||||||
</div>
|
</div>
|
||||||
${related.map((edge) => `
|
<div class="detail-section">
|
||||||
|
<p class="eyebrow">Relations</p>
|
||||||
|
${related.length ? related.map((edge) => `
|
||||||
<article class="result-card">
|
<article class="result-card">
|
||||||
<strong>${h(edge.source)} → ${h(edge.target)}</strong>
|
<strong>${h(edge.source)} → ${h(edge.target)}</strong>
|
||||||
<p>${h(edge.fact || edge.description || edge.predicate || "")}</p>
|
<p>${h(edge.fact || edge.description || edge.predicate || "")}</p>
|
||||||
</article>
|
</article>
|
||||||
`).join("")}
|
`).join("") : `<div class="empty-state">没有关联关系</div>`}
|
||||||
|
</div>
|
||||||
`);
|
`);
|
||||||
loadRelated(node.label).catch(() => relatedSearch.innerHTML = empty("相关检索加载失败"));
|
loadRelated(node.label).catch(() => {
|
||||||
|
detailPanel.insertAdjacentHTML("beforeend", `
|
||||||
|
<div class="detail-section">
|
||||||
|
<p class="eyebrow">Related</p>
|
||||||
|
<div class="empty-state">相关检索加载失败</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -432,7 +440,7 @@ function renderGraph(payload) {
|
||||||
line?.classList.add("active");
|
line?.classList.add("active");
|
||||||
const edge = edges.find((item) => item.id === el.dataset.edgeId);
|
const edge = edges.find((item) => item.id === el.dataset.edgeId);
|
||||||
renderInspector(`
|
renderInspector(`
|
||||||
<div class="detail-card">
|
<div class="detail-section">
|
||||||
<p class="eyebrow">Edge</p>
|
<p class="eyebrow">Edge</p>
|
||||||
<h3>${h(edge.source)} → ${h(edge.target)}</h3>
|
<h3>${h(edge.source)} → ${h(edge.target)}</h3>
|
||||||
<p>${h(edge.fact || edge.description || "暂无补充描述")}</p>
|
<p>${h(edge.fact || edge.description || "暂无补充描述")}</p>
|
||||||
|
|
@ -444,7 +452,14 @@ function renderGraph(payload) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`);
|
`);
|
||||||
loadRelated(`${edge.source} ${edge.predicate} ${edge.target}`).catch(() => relatedSearch.innerHTML = empty("相关检索加载失败"));
|
loadRelated(`${edge.source} ${edge.predicate} ${edge.target}`).catch(() => {
|
||||||
|
detailPanel.insertAdjacentHTML("beforeend", `
|
||||||
|
<div class="detail-section">
|
||||||
|
<p class="eyebrow">Related</p>
|
||||||
|
<div class="empty-state">相关检索加载失败</div>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,977 +0,0 @@
|
||||||
:root {
|
|
||||||
--primary: #5d67f5;
|
|
||||||
--primary-2: #7f8bff;
|
|
||||||
--primary-soft: #edf1ff;
|
|
||||||
--accent: #53c2da;
|
|
||||||
--bg: #f5f7ff;
|
|
||||||
--bg-2: #fbfcff;
|
|
||||||
--panel: rgba(255, 255, 255, 0.9);
|
|
||||||
--panel-strong: rgba(255, 255, 255, 0.96);
|
|
||||||
--border: rgba(212, 221, 247, 0.95);
|
|
||||||
--text: #22264d;
|
|
||||||
--muted: #68709d;
|
|
||||||
--danger: #b3261e;
|
|
||||||
--success: #11693c;
|
|
||||||
--shadow: 0 12px 28px rgba(73, 81, 141, 0.08);
|
|
||||||
--shadow-sm: 0 6px 16px rgba(73, 81, 141, 0.06);
|
|
||||||
--radius-xl: 20px;
|
|
||||||
--radius-lg: 16px;
|
|
||||||
--radius-md: 12px;
|
|
||||||
--radius-sm: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
* { box-sizing: border-box; }
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
margin: 0;
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--text);
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at 10% 10%, rgba(126, 186, 255, 0.16), transparent 24%),
|
|
||||||
radial-gradient(circle at 88% 14%, rgba(132, 121, 255, 0.12), transparent 22%),
|
|
||||||
linear-gradient(135deg, #f8faff 0%, var(--bg) 55%, var(--bg-2) 100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
a { color: inherit; text-decoration: none; }
|
|
||||||
|
|
||||||
button, input, textarea { font: inherit; }
|
|
||||||
|
|
||||||
.shell {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 220px minmax(0, 1fr);
|
|
||||||
gap: 14px;
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar, .panel, .detail-modal::backdrop {
|
|
||||||
backdrop-filter: blur(12px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 14px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 22px;
|
|
||||||
background: linear-gradient(180deg, rgba(236, 243, 255, 0.92), rgba(255, 255, 255, 0.8));
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand-mark {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 14px;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 17px;
|
|
||||||
font-weight: 800;
|
|
||||||
background: linear-gradient(135deg, var(--primary), var(--primary-2));
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand-kicker, .eyebrow {
|
|
||||||
margin: 0 0 3px;
|
|
||||||
color: var(--primary);
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: 0.08em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand h1, .panel h3, .dialog-head h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.brand h1 { font-size: 18px; }
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
display: grid;
|
|
||||||
gap: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link {
|
|
||||||
padding: 10px 12px;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
color: var(--muted);
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 700;
|
|
||||||
transition: 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-link:hover, .nav-link.active {
|
|
||||||
color: var(--primary);
|
|
||||||
border-color: rgba(109, 123, 255, 0.16);
|
|
||||||
background: rgba(255, 255, 255, 0.78);
|
|
||||||
}
|
|
||||||
|
|
||||||
.side-card, .panel {
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--radius-xl);
|
|
||||||
background: var(--panel);
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel { padding: 14px; }
|
|
||||||
|
|
||||||
.panel-head {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: start;
|
|
||||||
gap: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel h3 { font-size: 17px; }
|
|
||||||
|
|
||||||
.sidebar-shortcuts {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 6px;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-link, .chip {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 24px;
|
|
||||||
padding: 0 9px;
|
|
||||||
border-radius: 999px;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill-link {
|
|
||||||
background: rgba(255, 255, 255, 0.9);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chip {
|
|
||||||
background: var(--primary-soft);
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.chip.status-done, .chip.status-completed { background: #edfdf4; color: var(--success); }
|
|
||||||
.chip.status-pending, .chip.status-todo { background: #fff8e7; color: #b8860b; }
|
|
||||||
.chip.status-in_progress, .chip.status-active { background: #e8f4fd; color: #4a90d9; }
|
|
||||||
.chip.status-blocked { background: #fff4f2; color: var(--danger); }
|
|
||||||
|
|
||||||
.main {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 12px;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-toolbar {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16px;
|
|
||||||
padding: 16px 18px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 22px;
|
|
||||||
background:
|
|
||||||
radial-gradient(circle at top right, rgba(134, 144, 255, 0.12), transparent 28%),
|
|
||||||
linear-gradient(180deg, rgba(255, 255, 255, 0.94), rgba(244, 248, 255, 0.96));
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-toolbar h2 {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-toolbar-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn, .icon-btn {
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
min-height: 36px;
|
|
||||||
padding: 0 14px;
|
|
||||||
border-radius: 11px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #fff;
|
|
||||||
background: linear-gradient(135deg, var(--primary), var(--primary-2));
|
|
||||||
box-shadow: 0 8px 18px rgba(93, 103, 245, 0.18);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn:hover, .icon-btn:hover { transform: translateY(-1px); }
|
|
||||||
|
|
||||||
.btn:disabled {
|
|
||||||
opacity: 0.68;
|
|
||||||
cursor: not-allowed;
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn.ghost {
|
|
||||||
color: var(--primary);
|
|
||||||
background: rgba(255, 255, 255, 0.94);
|
|
||||||
box-shadow: none;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-grid, .content-grid, .workspace-grid {
|
|
||||||
display: grid;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
|
||||||
|
|
||||||
.highlight-card {
|
|
||||||
padding: 0;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
background: var(--panel-strong);
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight-card .hc-bar {
|
|
||||||
height: 4px;
|
|
||||||
background: var(--card-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight-card .eyebrow {
|
|
||||||
padding: 12px 14px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight-card strong {
|
|
||||||
display: block;
|
|
||||||
margin: 4px 0 2px;
|
|
||||||
padding: 0 14px;
|
|
||||||
font-size: 26px;
|
|
||||||
color: var(--card-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight-card p:last-child {
|
|
||||||
padding: 0 14px 14px;
|
|
||||||
margin: 0;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-grid {
|
|
||||||
grid-template-columns: minmax(330px, 1.1fr) minmax(340px, 1fr) minmax(220px, 0.72fr);
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box, .import-form, .import-fieldset {
|
|
||||||
display: grid;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.import-fieldset {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.import-fieldset:disabled { opacity: 0.6; }
|
|
||||||
|
|
||||||
.search-box input, .graph-controls input, textarea, input[type="file"] {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 38px;
|
|
||||||
padding: 9px 12px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 11px;
|
|
||||||
background: rgba(255, 255, 255, 0.94);
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
textarea {
|
|
||||||
min-height: 138px;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.field-label {
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-box {
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
border-radius: 12px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
background: rgba(255, 255, 255, 0.76);
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-box[data-kind="error"] {
|
|
||||||
color: var(--danger);
|
|
||||||
background: #fff4f2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-box[data-kind="success"] {
|
|
||||||
color: var(--success);
|
|
||||||
background: #edfdf4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-list, .search-results, .mini-stats, .card-list, .list-stack, .related-search {
|
|
||||||
display: grid;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-item, .mini-stat, .card, .list-item, .result-card, .detail-card {
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 14px;
|
|
||||||
background: rgba(255, 255, 255, 0.88);
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-item {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 24px 1fr;
|
|
||||||
gap: 8px;
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-index {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: var(--primary-soft);
|
|
||||||
color: var(--primary);
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mini-stat {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 10px 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ms-icon {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 15px;
|
|
||||||
background: color-mix(in srgb, var(--stat-color) 14%, transparent);
|
|
||||||
color: var(--stat-color);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ms-body strong {
|
|
||||||
display: block;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ms-body p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 11px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mini-stat strong, .card h4, .list-item strong, .result-card strong {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card { cursor: pointer; }
|
|
||||||
|
|
||||||
.card:hover, .result-card:hover, .list-item:hover {
|
|
||||||
border-color: rgba(120, 132, 255, 0.34);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
|
|
||||||
/* ── Meeting card ── */
|
|
||||||
|
|
||||||
.meeting-card {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 14px;
|
|
||||||
background: rgba(255, 255, 255, 0.88);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting-card:hover {
|
|
||||||
border-color: rgba(120, 132, 255, 0.34);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mc-date {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: var(--primary-soft);
|
|
||||||
color: var(--primary);
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
text-align: center;
|
|
||||||
line-height: 1.2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mc-body h4 {
|
|
||||||
margin: 0 0 4px;
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mc-body p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--muted);
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── List item with priority dot ── */
|
|
||||||
|
|
||||||
.list-item {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 14px;
|
|
||||||
background: rgba(255, 255, 255, 0.88);
|
|
||||||
}
|
|
||||||
|
|
||||||
.li-priority {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 4px;
|
|
||||||
border-radius: 2px;
|
|
||||||
background: var(--pri-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.li-body {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.li-body strong {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.li-body p {
|
|
||||||
margin: 0 0 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Metric card ── */
|
|
||||||
|
|
||||||
.metric-card {
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 14px;
|
|
||||||
background: rgba(255, 255, 255, 0.88);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mc-head {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mc-head strong {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mc-value {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.metric-card p {
|
|
||||||
margin: 0 0 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mc-bar-track {
|
|
||||||
height: 4px;
|
|
||||||
border-radius: 2px;
|
|
||||||
background: rgba(212, 221, 247, 0.5);
|
|
||||||
margin-bottom: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mc-bar-fill {
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 2px;
|
|
||||||
background: linear-gradient(90deg, var(--primary), var(--primary-2));
|
|
||||||
transition: width 0.4s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Series card ── */
|
|
||||||
|
|
||||||
.series-card {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 14px;
|
|
||||||
background: rgba(255, 255, 255, 0.88);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sc-count {
|
|
||||||
flex-shrink: 0;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
border-radius: 10px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 700;
|
|
||||||
background: var(--primary-soft);
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.sc-body strong {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sc-body p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Unified Import / Search panel ── */
|
|
||||||
|
|
||||||
.unified-panel {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unified-tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 4px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
padding: 3px;
|
|
||||||
border-radius: 11px;
|
|
||||||
background: rgba(212, 221, 247, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.unified-tab {
|
|
||||||
flex: 1;
|
|
||||||
padding: 7px 12px;
|
|
||||||
border: none;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 700;
|
|
||||||
cursor: pointer;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--muted);
|
|
||||||
transition: 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unified-tab.active {
|
|
||||||
background: #fff;
|
|
||||||
color: var(--primary);
|
|
||||||
box-shadow: 0 2px 6px rgba(73, 81, 141, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.unified-tab:hover:not(.active) {
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.unified-pane.hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Result card with kind badge ── */
|
|
||||||
|
|
||||||
.result-card {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rc-kind {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 1px 7px;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 10px;
|
|
||||||
font-weight: 700;
|
|
||||||
text-transform: uppercase;
|
|
||||||
background: var(--primary-soft);
|
|
||||||
color: var(--primary);
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.empty-state {
|
|
||||||
padding: 16px 14px;
|
|
||||||
text-align: center;
|
|
||||||
border: 1px dashed var(--border);
|
|
||||||
border-radius: 14px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-modal {
|
|
||||||
width: min(820px, calc(100vw - 24px));
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: 20px;
|
|
||||||
padding: 0;
|
|
||||||
background: rgba(255, 255, 255, 0.97);
|
|
||||||
box-shadow: var(--shadow);
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-modal::backdrop {
|
|
||||||
background: rgba(37, 44, 78, 0.28);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-head {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 16px 16px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-meta { padding: 0 16px 6px; color: var(--muted); }
|
|
||||||
|
|
||||||
.dialog-content {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0 16px 16px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
font-family: "Consolas", "Courier New", monospace;
|
|
||||||
max-height: 60vh;
|
|
||||||
overflow: auto;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-btn {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
border-radius: 10px;
|
|
||||||
background: rgba(242, 245, 255, 0.92);
|
|
||||||
color: var(--primary);
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Graph page ── */
|
|
||||||
|
|
||||||
.graph-shell {
|
|
||||||
height: 100vh;
|
|
||||||
overflow: hidden;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-shell .sidebar {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-shell .main {
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-shell .graph-layout {
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-shell .graph-layout .panel {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-layout {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 300px;
|
|
||||||
gap: 12px;
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-stage-panel {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-stage {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
position: relative;
|
|
||||||
background:
|
|
||||||
linear-gradient(180deg, rgba(251, 253, 255, 0.96), rgba(241, 246, 255, 0.94)),
|
|
||||||
radial-gradient(circle at center, rgba(133, 196, 255, 0.08), transparent 36%);
|
|
||||||
}
|
|
||||||
|
|
||||||
#graphSvg {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-panel {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 8px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-panel .detail-card,
|
|
||||||
.detail-panel .related-search {
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-card {
|
|
||||||
flex-shrink: 0;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-card strong {
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.related-search {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.related-search .result-card {
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Graph toolbar ── */
|
|
||||||
|
|
||||||
.graph-toolbar { padding: 8px 12px; }
|
|
||||||
|
|
||||||
.graph-controls {
|
|
||||||
display: flex;
|
|
||||||
gap: 6px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls .search-input {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 30px;
|
|
||||||
padding: 6px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls label.field-label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 2px;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls label.field-label input {
|
|
||||||
width: 44px;
|
|
||||||
min-height: 26px;
|
|
||||||
padding: 4px 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-controls .btn {
|
|
||||||
min-height: 30px;
|
|
||||||
padding: 0 12px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-toolbar-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 6px;
|
|
||||||
margin-top: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-actions {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
font-size: 11px;
|
|
||||||
color: var(--muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-type-filter {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
gap: 4px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-type-filter label {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 3px;
|
|
||||||
font-size: 11px;
|
|
||||||
color: var(--muted);
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-type-filter label input {
|
|
||||||
margin: 0;
|
|
||||||
accent-color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-meta { font-size: 11px; color: var(--muted); }
|
|
||||||
|
|
||||||
/* ── Graph nodes & edges ── */
|
|
||||||
|
|
||||||
.graph-node { cursor: pointer; }
|
|
||||||
|
|
||||||
.graph-node circle {
|
|
||||||
stroke: rgba(255, 255, 255, 0.85);
|
|
||||||
stroke-width: 2;
|
|
||||||
transition: filter 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-node--meeting circle { fill: #4a90d9; }
|
|
||||||
.graph-node--episode circle { fill: #34c759; }
|
|
||||||
.graph-node--entity circle { fill: var(--accent); }
|
|
||||||
.graph-node--fact circle { fill: #ff9500; }
|
|
||||||
|
|
||||||
.graph-node:hover circle { filter: brightness(1.2); }
|
|
||||||
|
|
||||||
.graph-node text {
|
|
||||||
font-size: 11px;
|
|
||||||
fill: var(--text);
|
|
||||||
pointer-events: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-edge {
|
|
||||||
stroke: rgba(120, 136, 194, 0.42);
|
|
||||||
stroke-width: 1.6;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: stroke 0.15s, stroke-width 0.15s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edge-wrap:hover .graph-edge {
|
|
||||||
stroke: rgba(120, 136, 194, 0.7);
|
|
||||||
stroke-width: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-edge.active {
|
|
||||||
stroke: var(--primary);
|
|
||||||
stroke-width: 2.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.edge-wrap text {
|
|
||||||
pointer-events: none;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Legend ── */
|
|
||||||
|
|
||||||
.legend { font-size: 11px; color: var(--muted); }
|
|
||||||
|
|
||||||
.legend-dot {
|
|
||||||
display: inline-block;
|
|
||||||
width: 9px;
|
|
||||||
height: 9px;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.legend-dot.meeting { background: #4a90d9; }
|
|
||||||
.legend-dot.episode { background: #34c759; }
|
|
||||||
.legend-dot.entity { background: var(--accent); }
|
|
||||||
.legend-dot.fact { background: #ff9500; }
|
|
||||||
|
|
||||||
.graph-shell .sidebar {
|
|
||||||
gap: 8px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-shell .sidebar .legend {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 3px;
|
|
||||||
font-size: 11px;
|
|
||||||
padding: 0 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.graph-shell .sidebar .legend .eyebrow {
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Graph controls overlay ── */
|
|
||||||
|
|
||||||
.zoom-reset-btn, .pause-btn {
|
|
||||||
font-size: 11px;
|
|
||||||
min-height: 28px;
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zoom-hint {
|
|
||||||
font-size: 11px;
|
|
||||||
color: var(--muted);
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Responsive ── */
|
|
||||||
|
|
||||||
@media (max-width: 1240px) {
|
|
||||||
.shell, .graph-shell, .dashboard-grid, .content-grid, .graph-layout, .stats-grid {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar { order: 2; }
|
|
||||||
|
|
||||||
.graph-shell { height: auto; overflow: auto; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 720px) {
|
|
||||||
.shell, .graph-shell {
|
|
||||||
padding: 10px;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar, .panel { border-radius: 18px; }
|
|
||||||
|
|
||||||
.search-box { grid-template-columns: 1fr; }
|
|
||||||
|
|
||||||
.graph-stage { min-height: 250px; }
|
|
||||||
|
|
||||||
.graph-controls { flex-wrap: wrap; }
|
|
||||||
|
|
||||||
.graph-controls .search-input { min-width: 100%; }
|
|
||||||
}
|
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Neo4j Graph Explorer</title>
|
<title>图谱浏览 — Meeting Memory</title>
|
||||||
<link rel="stylesheet" href="/styles.css">
|
<link rel="stylesheet" href="/static_v2/styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="shell graph-shell">
|
<div class="shell graph-shell">
|
||||||
|
<!-- ====== Sidebar ====== -->
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<div class="brand-mark">G</div>
|
<div class="brand-mark">G</div>
|
||||||
|
|
@ -18,12 +19,12 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<a class="nav-link" href="/index.html">总览面板</a>
|
<a class="nav-link" href="/">总览面板</a>
|
||||||
<a class="nav-link active" href="/graph.html">图谱浏览</a>
|
<a class="nav-link active" href="/graph">图谱浏览</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="legend">
|
<div class="legend">
|
||||||
<p class="eyebrow" style="margin-bottom:6px">图例</p>
|
<p class="eyebrow">图例</p>
|
||||||
<span><i class="legend-dot meeting"></i>会议</span>
|
<span><i class="legend-dot meeting"></i>会议</span>
|
||||||
<span><i class="legend-dot episode"></i>片段</span>
|
<span><i class="legend-dot episode"></i>片段</span>
|
||||||
<span><i class="legend-dot entity"></i>实体</span>
|
<span><i class="legend-dot entity"></i>实体</span>
|
||||||
|
|
@ -31,12 +32,20 @@
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<!-- ====== Main ====== -->
|
||||||
<main class="main">
|
<main class="main">
|
||||||
<div class="graph-toolbar panel">
|
<!-- Graph Toolbar -->
|
||||||
|
<div class="panel graph-toolbar">
|
||||||
<form class="graph-controls" id="graphSearchForm">
|
<form class="graph-controls" id="graphSearchForm">
|
||||||
<input id="graphQueryInput" type="text" placeholder="搜索节点名称或关键词…" class="search-input">
|
<input id="graphQueryInput" type="text" placeholder="搜索节点名称或关键词…" class="search-input">
|
||||||
<label class="field-label">节点 <input id="graphNodeLimit" type="number" min="10" max="200" step="10" value="60"></label>
|
<label class="field-label">
|
||||||
<label class="field-label">关系 <input id="graphEdgeLimit" type="number" min="10" max="300" step="10" value="120"></label>
|
节点
|
||||||
|
<input id="graphNodeLimit" type="number" min="10" max="200" step="10" value="60">
|
||||||
|
</label>
|
||||||
|
<label class="field-label">
|
||||||
|
关系
|
||||||
|
<input id="graphEdgeLimit" type="number" min="10" max="300" step="10" value="120">
|
||||||
|
</label>
|
||||||
<button class="btn" type="submit">更新</button>
|
<button class="btn" type="submit">更新</button>
|
||||||
</form>
|
</form>
|
||||||
<div class="graph-toolbar-row">
|
<div class="graph-toolbar-row">
|
||||||
|
|
@ -47,19 +56,19 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Graph Layout -->
|
||||||
<div class="graph-layout">
|
<div class="graph-layout">
|
||||||
|
<!-- Graph Stage -->
|
||||||
<div class="panel graph-stage-panel">
|
<div class="panel graph-stage-panel">
|
||||||
<div class="graph-stage" id="graphStage">
|
<div class="graph-stage" id="graphStage">
|
||||||
<svg id="graphSvg" viewBox="0 0 960 640" preserveAspectRatio="xMidYMid meet"></svg>
|
<svg id="graphSvg" viewBox="0 0 960 640" preserveAspectRatio="xMidYMid meet"></svg>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="panel detail-panel">
|
<!-- Detail Panel -->
|
||||||
<div class="detail-card" id="graphDetail">
|
<div class="panel detail-panel" id="detailPanel">
|
||||||
<div class="empty-state">点击节点或关系查看详情</div>
|
<div class="empty-state">点击节点或关系查看详情</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="related-search" id="relatedSearch"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -3,11 +3,12 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Meeting Memory Console</title>
|
<title>会议记忆中枢 — Meeting Memory</title>
|
||||||
<link rel="stylesheet" href="/styles.css">
|
<link rel="stylesheet" href="/static_v2/styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="shell">
|
<div class="shell">
|
||||||
|
<!-- ====== Sidebar ====== -->
|
||||||
<aside class="sidebar">
|
<aside class="sidebar">
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
<div class="brand-mark">M</div>
|
<div class="brand-mark">M</div>
|
||||||
|
|
@ -18,18 +19,20 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav class="nav">
|
<nav class="nav">
|
||||||
<a class="nav-link active" href="/index.html">总览面板</a>
|
<a class="nav-link active" href="/">总览面板</a>
|
||||||
<a class="nav-link" href="/graph.html">图谱浏览</a>
|
<a class="nav-link" href="/graph">图谱浏览</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="side-card sidebar-shortcuts">
|
<div class="sidebar-shortcuts">
|
||||||
<a class="pill-link" href="#import-panel">导入会议</a>
|
<a class="pill-link" href="#import-panel">导入会议</a>
|
||||||
<a class="pill-link" href="#search-panel">知识检索</a>
|
<a class="pill-link" href="#search-panel">知识检索</a>
|
||||||
<a class="pill-link" href="/graph.html">图谱页</a>
|
<a class="pill-link" href="/graph">图谱页</a>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<!-- ====== Main ====== -->
|
||||||
<main class="main">
|
<main class="main">
|
||||||
|
<!-- Toolbar -->
|
||||||
<div class="main-toolbar">
|
<div class="main-toolbar">
|
||||||
<div>
|
<div>
|
||||||
<p class="eyebrow">Dashboard</p>
|
<p class="eyebrow">Dashboard</p>
|
||||||
|
|
@ -40,8 +43,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Highlight Cards -->
|
||||||
<section class="stats-grid" id="highlightGrid"></section>
|
<section class="stats-grid" id="highlightGrid"></section>
|
||||||
|
|
||||||
|
<!-- Unified Panel: Import / Search / Stats -->
|
||||||
<section class="panel unified-panel">
|
<section class="panel unified-panel">
|
||||||
<div class="unified-tabs">
|
<div class="unified-tabs">
|
||||||
<button class="unified-tab active" data-tab="import">导入</button>
|
<button class="unified-tab active" data-tab="import">导入</button>
|
||||||
|
|
@ -49,6 +54,7 @@
|
||||||
<button class="unified-tab" data-tab="stats">统计</button>
|
<button class="unified-tab" data-tab="stats">统计</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Import Pane -->
|
||||||
<div class="unified-pane" id="unifiedImport">
|
<div class="unified-pane" id="unifiedImport">
|
||||||
<form class="import-form" id="importForm">
|
<form class="import-form" id="importForm">
|
||||||
<fieldset id="importFieldset" class="import-fieldset">
|
<fieldset id="importFieldset" class="import-fieldset">
|
||||||
|
|
@ -73,6 +79,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Search Pane -->
|
||||||
<div class="unified-pane hidden" id="unifiedSearch">
|
<div class="unified-pane hidden" id="unifiedSearch">
|
||||||
<form class="search-box" id="searchForm">
|
<form class="search-box" id="searchForm">
|
||||||
<input id="searchInput" type="text" placeholder="搜索会议主题、负责人、指标、关系事实...">
|
<input id="searchInput" type="text" placeholder="搜索会议主题、负责人、指标、关系事实...">
|
||||||
|
|
@ -83,11 +90,13 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Stats Pane -->
|
||||||
<div class="unified-pane hidden" id="unifiedStats">
|
<div class="unified-pane hidden" id="unifiedStats">
|
||||||
<div class="mini-stats" id="statsList"></div>
|
<div class="mini-stats" id="statsList"></div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Content Grid -->
|
||||||
<div class="content-grid">
|
<div class="content-grid">
|
||||||
<section class="panel" id="meeting-list">
|
<section class="panel" id="meeting-list">
|
||||||
<div class="panel-head">
|
<div class="panel-head">
|
||||||
|
|
@ -124,6 +133,7 @@
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Meeting Detail Dialog -->
|
||||||
<dialog class="detail-modal" id="meetingDialog">
|
<dialog class="detail-modal" id="meetingDialog">
|
||||||
<div class="dialog-head">
|
<div class="dialog-head">
|
||||||
<div>
|
<div>
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue