mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-09-10 04:00:35 +00:00
## Folder & Project
- Renamed `semantica-explorer/` → `explorer/` (cleaner path)
- Browser tab title: `Semantica Knowledge Explorer`
- Brand pill: `SEM` → `SKE` (tooltip: Semantica Knowledge Explorer)
- Nav rail label: `Explore` → `Knowledge Explorer`
- package.json name: `semantica-knowledge-explorer`
- Downgraded Vite 8 → Vite 5 for Node v20.17.0 compatibility
## App Shell
- Dynamic per-workspace kicker labels replacing static "Workspace" pill:
Graph Studio · Vocabulary Browser · Reasoning Engine · SPARQL Query ·
Decision Intelligence · Knowledge Audit · Graph Governance
## Enrich Workspace — 2 new tabs
### Entity Resolution tab
- Similarity threshold slider (0.50–0.99)
- Run Dedup Scan → POST /api/enrich/dedup
- Flagged pairs list with colour-coded score bars (red/amber/green)
- Expandable inline diff: primary vs duplicate side-by-side
- One-click Merge → POST /api/enrich/merge with logEvent dispatch
- Dismiss per pair; Clear all button
- Merge history sidebar pulled live from Registry store
### Registry tab (Document Registry)
- Live chronological audit log of all KG mutations in-session
- Colour-coded op-type badges: IMPORT · MERGE · ADD NODE · ADD EDGE ·
INFER · DELETE · EXPORT · VOCAB
- Filter pills to narrow by operation type
- Expandable JSON detail rows per entry
- Clear log button
- Entirely client-side via registryStore (no backend needed)
## Manage Workspace — 2 new tabs
### KG Overview tab
- Stats chips: total nodes, edges, graph density
- Node type breakdown bar chart (up to 8 types, colour-coded)
- Edge type breakdown bar chart from /api/graph/stats
- Top-10 most connected nodes ranked by degree
- Skeleton loading states + Refresh button
### Ontology Summary tab
- Read-only SKOS scheme tree (scheme → top concepts → narrower)
- Concept detail panel: labels, notation, description, narrower nav
- "Open Full Browser" button deep-links to Vocabulary Browser tab
## Decision Workspace polish
- CausalFlowDiagram: vertical node cards connected by relationship pills
- Outcome badges: colour-coded (green=approved, red=rejected, amber=deferred)
- Live filter input across decision ID, category, and outcome
- Animated skeleton loading while list fetches
## Graph Inspector polish
- PathFlowViz: clickable node chips connected by edge-type labels;
clicking a chip focuses that node in the canvas
- Link Prediction button shows spinner while computing
- Empty states for path trace and candidate links sections
## Registry dispatch — WebSocket
- ADD_NODE events → logEvent("add-node", …) in GraphWorkspace WS handler
- ADD_EDGE events → logEvent("add-edge", …) in GraphWorkspace WS handler
- Import, Export, Merge already dispatched logEvent on API response
## Graph visibility overhaul
### Edge colours (were nearly transparent, now clearly visible)
- edgeBackbone: rgba(…, 0.04) → rgba(…, 0.38)
- edgeStructure: rgba(…, 0.009) → rgba(…, 0.28)
- edgeInspection: rgba(…, 0.026) → rgba(…, 0.48)
- Muted edges: 0.009–0.02 → 0.12–0.26
- Focus edges: 0.16 → 0.42
### Edge sizes
- default minSize: 0.18 → 0.9 (always at least 1 pixel wide)
- path minSize: 1.8 → 2.4
- inactive/muted: hide:true → hide:false (dimmed not hidden)
### Node sizes
- default sizeMultiplier: 0.72 → 0.92
- default minSize: 0.68 → 3.5 (visible at all zoom levels)
- overview nodeScale: 0.66 → 0.88
- nodeTintMix (colour): 0.03 → 0.14
- nodeCoreMix (brightness): 0.52 → 0.72
### Label budget
- overview: 10 → 28 labels
- structure: 36 → 60 labels
- inspection: 80 → 120 labels
### Sigma settings
- renderEdgeLabels: false → true (relationship type on every edge)
- edgeLabelSize: — → 10
- labelRenderedSizeThreshold: 4 → 2
- labelDensity: 0.86 → 1.1
- hideLabelsOnMove: true → false (labels stay visible while panning)
- hideEdgesOnMove: true → false (edges stay visible while panning)
- minCameraRatio: — → 0.04 (prevents zooming inside a node)
- maxCameraRatio: — → 8 (graph stays visible when zoomed out)
### Zoom controls
- Added Zoom In (+) and Zoom Out (−) buttons to graph toolbar
- Smooth animated zoom via camera.animatedZoom / animatedUnzoom (200ms)
- Mouse scroll wheel clamped between minCameraRatio and maxCameraRatio
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
132 lines
3.4 KiB
TypeScript
132 lines
3.4 KiB
TypeScript
import type { CSSProperties } from "react";
|
|
|
|
import type { GraphPlugin } from "./types";
|
|
|
|
const LEGEND_PANEL_ID = "legend-panel";
|
|
const MAX_GROUPS = 8;
|
|
|
|
export const legendPlugin: GraphPlugin = {
|
|
id: "legend",
|
|
mount: () => {},
|
|
unmount: () => {},
|
|
onStateChange: () => {},
|
|
toolbarItems: (context) => [
|
|
{
|
|
id: "legend-toggle",
|
|
label: "Legend",
|
|
title: "Toggle semantic legend",
|
|
active: context.isPanelOpen(LEGEND_PANEL_ID),
|
|
order: 20,
|
|
onClick: () => context.dispatchAction({ type: "togglePanel", panelId: LEGEND_PANEL_ID }),
|
|
},
|
|
],
|
|
renderPanel: (context) => {
|
|
if (!context.isPanelOpen(LEGEND_PANEL_ID)) {
|
|
return null;
|
|
}
|
|
|
|
const groups = new Map<string, { count: number; color: string }>();
|
|
context.graph.forEachNode((_nodeId, attrs) => {
|
|
const semanticGroup = String(attrs.semanticGroup || attrs.nodeType || "entity");
|
|
const color = String(attrs.baseColor || context.theme.palette.semantic[0]);
|
|
const current = groups.get(semanticGroup);
|
|
groups.set(semanticGroup, {
|
|
count: (current?.count ?? 0) + 1,
|
|
color,
|
|
});
|
|
});
|
|
|
|
const items = [...groups.entries()]
|
|
.map(([group, data]) => ({ group, ...data }))
|
|
.sort((left, right) => right.count - left.count)
|
|
.slice(0, MAX_GROUPS);
|
|
|
|
return {
|
|
id: LEGEND_PANEL_ID,
|
|
title: "Legend",
|
|
placement: "bottom",
|
|
order: 10,
|
|
defaultOpen: false,
|
|
preferredWidth: 320,
|
|
preferredHeight: 220,
|
|
content: (
|
|
<div style={panelBodyStyle}>
|
|
<div style={panelEyebrowStyle}>Semantic groups</div>
|
|
{items.length ? (
|
|
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
|
|
{items.map((item) => (
|
|
<div key={item.group} style={legendRowStyle}>
|
|
<span
|
|
style={{
|
|
...swatchStyle,
|
|
background: item.color,
|
|
boxShadow: `0 0 0 1px rgba(255,255,255,0.06), 0 0 18px ${item.color}44`,
|
|
}}
|
|
/>
|
|
<div style={{ minWidth: 0, flex: 1 }}>
|
|
<div style={rowTitleStyle}>{item.group}</div>
|
|
<div style={rowMetaStyle}>{item.count.toLocaleString()} nodes</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div style={emptyTextStyle}>Legend will populate when the graph metadata is available.</div>
|
|
)}
|
|
</div>
|
|
),
|
|
};
|
|
},
|
|
};
|
|
|
|
const panelBodyStyle: CSSProperties = {
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
gap: 12,
|
|
};
|
|
|
|
const panelEyebrowStyle: CSSProperties = {
|
|
color: "#8ea4be",
|
|
fontSize: 11,
|
|
fontWeight: 700,
|
|
letterSpacing: "0.08em",
|
|
textTransform: "uppercase",
|
|
};
|
|
|
|
const legendRowStyle: CSSProperties = {
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 10,
|
|
padding: "8px 10px",
|
|
borderRadius: 12,
|
|
border: "1px solid rgba(255,255,255,0.06)",
|
|
background: "rgba(255,255,255,0.025)",
|
|
};
|
|
|
|
const swatchStyle: CSSProperties = {
|
|
width: 10,
|
|
height: 10,
|
|
borderRadius: 999,
|
|
flexShrink: 0,
|
|
};
|
|
|
|
const rowTitleStyle: CSSProperties = {
|
|
color: "#f3f7fd",
|
|
fontSize: 13,
|
|
fontWeight: 600,
|
|
overflow: "hidden",
|
|
textOverflow: "ellipsis",
|
|
whiteSpace: "nowrap",
|
|
};
|
|
|
|
const rowMetaStyle: CSSProperties = {
|
|
color: "#8ea4be",
|
|
fontSize: 12,
|
|
};
|
|
|
|
const emptyTextStyle: CSSProperties = {
|
|
color: "#8ea4be",
|
|
fontSize: 12,
|
|
lineHeight: 1.5,
|
|
};
|