feat: update knowledge and agent UI

This commit is contained in:
吉鑫升 committed 2026-06-11 16:18:38 +08:00
1 parent bebe4feccf
commit 7efd811e75
27 files changed
+2949 -782

No files matched your search

@@ -1,4 +1,5 @@
import { Search } from "lucide-react";
import { ChevronDown, ChevronRight, Search, X } from "lucide-react";
import { useState } from "react";
import type { GraphNode, GraphStats, NodeKind } from "./graphTypes";
import {
@@ -24,9 +25,56 @@ interface GraphSidebarProps {
onToggleEdgeLabel: (label: string) => void;
onRestoreHidden: () => void;
onPickNode: (id: string) => void;
onClose: () => void;
}
type SectionKey = "nodeTypes" | "edgeTypes" | "graphStats" | "coreNodes";
interface FilterSectionProps {
title: string;
extra?: string;
collapsed: boolean;
onToggle: () => void;
children: React.ReactNode;
}
function FilterSection(props: FilterSectionProps) {
return (
<section className="kg-filter-section">
<button
type="button"
className="kg-filter-section-header"
onClick={props.onToggle}
aria-expanded={!props.collapsed}
>
<span>{props.title}</span>
<span className="kg-filter-section-extra">
{props.extra}
{props.collapsed ? <ChevronRight size={14} /> : <ChevronDown size={14} />}
</span>
</button>
{!props.collapsed ? (
<div className="kg-filter-section-body">{props.children}</div>
) : null}
</section>
);
}
export function GraphSidebar(props: GraphSidebarProps) {
// 节点类型 / 关系类型默认收起,统计与核心节点默认展开。
const [collapsedSections, setCollapsedSections] = useState<
Record<SectionKey, boolean>
>({
nodeTypes: true,
edgeTypes: true,
graphStats: false,
coreNodes: false,
});
const toggleFilterSection = (key: SectionKey) => {
setCollapsedSections((prev) => ({ ...prev, [key]: !prev[key] }));
};
const kinds = NODE_KIND_ORDER.filter((k) => (props.nodeKindCounts.get(k) ?? 0) > 0);
const edgeLabels = [...props.edgeLabelCounts.entries()].sort((a, b) => b[1] - a[1]);
@@ -34,15 +82,25 @@ export function GraphSidebar(props: GraphSidebarProps) {
<aside className="kg-panel kg-sidebar">
<div className="kg-panel-header">
<span className="kg-panel-title">搜索与筛选</span>
{props.hiddenCount > 0 ? (
<div className="kg-panel-header-actions">
{props.hiddenCount > 0 ? (
<button
type="button"
className="kg-text-button"
onClick={props.onRestoreHidden}
>
恢复 {props.hiddenCount} 个隐藏节点
</button>
) : null}
<button
type="button"
className="kg-text-button"
onClick={props.onRestoreHidden}
className="kg-panel-close-btn"
title="收起筛选面板"
onClick={props.onClose}
>
恢复 {props.hiddenCount} 个隐藏节点
<X size={15} />
</button>
) : null}
</div>
</div>
<div className="kg-panel-content">
<div className="kg-search-wrap">
@@ -67,8 +125,12 @@ export function GraphSidebar(props: GraphSidebarProps) {
</div>
) : null}
<section className="kg-filter-section">
<div className="kg-filter-title">节点类型</div>
<FilterSection
title="节点类型"
extra={`${kinds.length} 类`}
collapsed={collapsedSections.nodeTypes}
onToggle={() => toggleFilterSection("nodeTypes")}
>
{kinds.map((kind) => (
<label key={kind} className="kg-check-row">
<span className="kg-check-row-main">
@@ -86,11 +148,15 @@ export function GraphSidebar(props: GraphSidebarProps) {
<span className="kg-count">{props.nodeKindCounts.get(kind)}</span>
</label>
))}
</section>
</FilterSection>
{edgeLabels.length > 0 ? (
<section className="kg-filter-section">
<div className="kg-filter-title">关系类型</div>
<FilterSection
title="关系类型"
extra={`${edgeLabels.length} 类`}
collapsed={collapsedSections.edgeTypes}
onToggle={() => toggleFilterSection("edgeTypes")}
>
{edgeLabels.map(([label, count]) => {
const info = getEdgeInfo(label);
return (
@@ -114,11 +180,14 @@ export function GraphSidebar(props: GraphSidebarProps) {
</label>
);
})}
</section>
</FilterSection>
) : null}
<section className="kg-filter-section">
<div className="kg-filter-title">图谱统计</div>
<FilterSection
title="图谱统计"
collapsed={collapsedSections.graphStats}
onToggle={() => toggleFilterSection("graphStats")}
>
<div className="kg-stats-grid">
<div className="kg-stat-card">
<div className="kg-stat-card-value">{props.stats.nodes}</div>
@@ -139,11 +208,15 @@ export function GraphSidebar(props: GraphSidebarProps) {
<div className="kg-stat-card-label">最大连通分量</div>
</div>
</div>
</section>
</FilterSection>
{props.topNodes.length > 0 ? (
<section className="kg-filter-section">
<div className="kg-filter-title">核心节点</div>
<FilterSection
title="核心节点"
extra={`${props.topNodes.length} 个`}
collapsed={collapsedSections.coreNodes}
onToggle={() => toggleFilterSection("coreNodes")}
>
{props.topNodes.map(({ node, degree }, i) => (
<div
key={node.id}
@@ -155,7 +228,7 @@ export function GraphSidebar(props: GraphSidebarProps) {
<span className="kg-rank-degree">{degree} 度</span>
</div>
))}
</section>
</FilterSection>
) : null}
</div>
</aside>