mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 00:08:27 +00:00
* Feat: clustering fact statements * Feat: cluster drift * Feat: add recall count and model to search * Feat: Github integration * Fix: clustering UI * Improve graph * Bump: new version --------- Co-authored-by: Manoj K <saimanoj58@gmail.com>
154 lines
4.1 KiB
TypeScript
154 lines
4.1 KiB
TypeScript
import type {
|
|
Node,
|
|
Edge,
|
|
GraphNode,
|
|
GraphEdge,
|
|
RawTriplet,
|
|
GraphTriplet,
|
|
} from "./type";
|
|
|
|
export function toGraphNode(node: Node): GraphNode {
|
|
const primaryLabel =
|
|
node.labels?.find((label) => label != "Entity") || "Entity";
|
|
|
|
return {
|
|
id: node.uuid,
|
|
value: node.name,
|
|
uuid: node.uuid,
|
|
name: node.name,
|
|
createdAt: node.createdAt,
|
|
attributes: node.attributes,
|
|
summary: node.summary,
|
|
labels: node.labels,
|
|
primaryLabel,
|
|
clusterId: node?.clusterId, // Extract cluster ID from attributes
|
|
};
|
|
}
|
|
|
|
export function toGraphEdge(edge: Edge): GraphEdge {
|
|
return {
|
|
id: edge.uuid,
|
|
value: edge.type,
|
|
...edge,
|
|
};
|
|
}
|
|
|
|
export function toGraphTriplet(triplet: RawTriplet): GraphTriplet {
|
|
return {
|
|
source: toGraphNode(triplet.sourceNode),
|
|
relation: toGraphEdge(triplet.edge),
|
|
target: toGraphNode(triplet.targetNode),
|
|
};
|
|
}
|
|
|
|
export function toGraphTriplets(triplets: RawTriplet[]): GraphTriplet[] {
|
|
return triplets.map(toGraphTriplet);
|
|
}
|
|
|
|
export function drawRoundRect(
|
|
ctx: CanvasRenderingContext2D,
|
|
x: number,
|
|
y: number,
|
|
width: number,
|
|
height: number,
|
|
radius: number,
|
|
): void {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x + radius, y);
|
|
ctx.lineTo(x + width - radius, y);
|
|
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
|
ctx.lineTo(x + width, y + height - radius);
|
|
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
|
ctx.lineTo(x + radius, y + height);
|
|
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
|
ctx.lineTo(x, y + radius);
|
|
ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
ctx.closePath();
|
|
}
|
|
|
|
const TEXT_COLOR = "#000000";
|
|
|
|
export function drawHover(
|
|
context: CanvasRenderingContext2D,
|
|
data: any,
|
|
settings: any,
|
|
) {
|
|
const size = settings.labelSize;
|
|
const font = settings.labelFont;
|
|
const weight = settings.labelWeight;
|
|
const subLabelSize = size - 2;
|
|
|
|
const label = data.label;
|
|
const subLabel = data.tag !== "unknown" ? data.tag : "";
|
|
const entityLabel = data.nodeData.attributes.nodeType;
|
|
|
|
// Simulate the --shadow-1 Tailwind shadow:
|
|
// lch(0 0 0 / 0.022) 0px 3px 6px -2px, lch(0 0 0 / 0.044) 0px 1px 1px;
|
|
// Canvas only supports a single shadow, so we approximate with the stronger one.
|
|
// lch(0 0 0 / 0.044) is roughly rgba(0,0,0,0.044)
|
|
context.beginPath();
|
|
context.fillStyle = "#fff";
|
|
context.shadowOffsetX = 0;
|
|
context.shadowOffsetY = 1;
|
|
context.shadowBlur = 1;
|
|
context.shadowColor = "rgba(0,0,0,0.044)";
|
|
|
|
context.font = `${weight} ${size}px ${font}`;
|
|
const labelWidth = context.measureText(label).width;
|
|
context.font = `${weight} ${subLabelSize}px ${font}`;
|
|
const subLabelWidth = subLabel ? context.measureText(subLabel).width : 0;
|
|
context.font = `${weight} ${subLabelSize}px ${font}`;
|
|
const entityLabelWidth = entityLabel
|
|
? context.measureText(entityLabel).width
|
|
: 0;
|
|
|
|
const textWidth = Math.max(labelWidth, subLabelWidth, entityLabelWidth);
|
|
|
|
const x = Math.round(data.x);
|
|
const y = Math.round(data.y);
|
|
const w = Math.round(textWidth + size / 2 + data.size + 3);
|
|
const hLabel = Math.round(size / 2 + 4);
|
|
const hSubLabel = subLabel ? Math.round(subLabelSize / 2 + 9) : 0;
|
|
const hentityLabel = Math.round(subLabelSize / 2 + 9);
|
|
|
|
drawRoundRect(
|
|
context,
|
|
x,
|
|
y - hSubLabel - 12,
|
|
w,
|
|
hentityLabel + hLabel + hSubLabel + 12,
|
|
5,
|
|
);
|
|
context.closePath();
|
|
context.fill();
|
|
|
|
// Remove shadow for text
|
|
context.shadowOffsetX = 0;
|
|
context.shadowOffsetY = 0;
|
|
context.shadowBlur = 0;
|
|
context.shadowColor = "transparent";
|
|
|
|
// And finally we draw the labels
|
|
context.fillStyle = TEXT_COLOR;
|
|
context.font = `${weight} ${size}px ${font}`;
|
|
context.fillText(label, data.x + data.size + 3, data.y + size / 3);
|
|
|
|
if (subLabel) {
|
|
context.fillStyle = TEXT_COLOR;
|
|
context.font = `${weight} ${subLabelSize}px ${font}`;
|
|
context.fillText(
|
|
subLabel,
|
|
data.x + data.size + 3,
|
|
data.y - (2 * size) / 3 - 2,
|
|
);
|
|
}
|
|
|
|
context.fillStyle = data.color;
|
|
context.font = `${weight} ${subLabelSize}px ${font}`;
|
|
context.fillText(
|
|
entityLabel,
|
|
data.x + data.size + 3,
|
|
data.y + size / 3 + 3 + subLabelSize,
|
|
);
|
|
}
|