mirror of
https://github.com/FranP-code/Reflecto.git
synced 2025-10-13 00:43:31 +00:00
102 lines
4.1 KiB
TypeScript
102 lines
4.1 KiB
TypeScript
import { generateText } from "./processing";
|
|
import { KnowledgeGraphManager } from "./knowledge-graph";
|
|
import { createAITextResult } from "./ai-shapes";
|
|
|
|
export function extractTextContentFromShape(editor: any, shape: any): string {
|
|
if (!shape) return "";
|
|
if (shape.type === "ai-text-result") return shape.props?.content ?? "";
|
|
if (shape.type === "ai-image") return shape.props?.extractedText ?? "";
|
|
if (shape.type === "text" || shape.type === "note") return shape.props?.text ?? "";
|
|
return "";
|
|
}
|
|
|
|
export async function generateRelationText(textA: string, textB: string): Promise<string> {
|
|
const userPrompt = [
|
|
"Given two short texts, write a brief plain-text relation between them.",
|
|
"One line only. No markdown, no lists, no headings.",
|
|
"Return only the relation phrase/sentence.",
|
|
"",
|
|
`Text A: "${textA}"`,
|
|
`Text B: "${textB}"`,
|
|
].join("\n");
|
|
const text = await generateText(userPrompt, 0.4);
|
|
return (text ?? "").trim();
|
|
}
|
|
|
|
export function midpointBetweenShapes(editor: any, aId: string, bId: string): { x: number; y: number } {
|
|
const aBounds = editor.getShapePageBounds(aId);
|
|
const bBounds = editor.getShapePageBounds(bId);
|
|
const aShape = editor.getShape(aId);
|
|
const bShape = editor.getShape(bId);
|
|
const acx = aBounds ? aBounds.x + aBounds.w / 2 : (aShape?.x ?? 0);
|
|
const acy = aBounds ? aBounds.y + aBounds.h / 2 : (aShape?.y ?? 0);
|
|
const bcx = bBounds ? bBounds.x + bBounds.w / 2 : (bShape?.x ?? 0);
|
|
const bcy = bBounds ? bBounds.y + bBounds.h / 2 : (bShape?.y ?? 0);
|
|
const midx = (acx + bcx) / 2;
|
|
const midy = (acy + bcy) / 2;
|
|
const dx = bcx - acx;
|
|
const dy = bcy - acy;
|
|
const len = Math.hypot(dx, dy) || 1;
|
|
// Perpendicular unit vector
|
|
const px = -dy / len;
|
|
const py = dx / len;
|
|
const offset = Math.min(120, Math.max(40, len * 0.12));
|
|
return { x: midx + px * offset, y: midy + py * offset };
|
|
}
|
|
|
|
export async function insertRelationBetweenShapes(
|
|
editor: any,
|
|
aId: string,
|
|
bId: string,
|
|
relationText: string
|
|
) {
|
|
const aBounds = editor.getShapePageBounds(aId);
|
|
const bBounds = editor.getShapePageBounds(bId);
|
|
const aShape = editor.getShape(aId);
|
|
const bShape = editor.getShape(bId);
|
|
const acx = aBounds ? aBounds.x + aBounds.w / 2 : (aShape?.x ?? 0);
|
|
const acy = aBounds ? aBounds.y + aBounds.h / 2 : (aShape?.y ?? 0);
|
|
const bcx = bBounds ? bBounds.x + bBounds.w / 2 : (bShape?.x ?? 0);
|
|
const bcy = bBounds ? bBounds.y + bBounds.h / 2 : (bShape?.y ?? 0);
|
|
const dx = bcx - acx;
|
|
const dy = bcy - acy;
|
|
const len = Math.hypot(dx, dy) || 1;
|
|
const px = -dy / len;
|
|
const py = dx / len;
|
|
|
|
let baseOffset = Math.min(120, Math.max(40, len * 0.12));
|
|
let pos = { x: (acx + bcx) / 2 + px * baseOffset, y: (acy + bcy) / 2 + py * baseOffset };
|
|
|
|
let relationId = "";
|
|
editor.batch?.(() => {
|
|
relationId = createAITextResult(editor, {
|
|
fromShapeId: null,
|
|
sourceType: "analysis",
|
|
content: relationText.trim(),
|
|
x: pos.x,
|
|
y: pos.y,
|
|
});
|
|
|
|
// Nudge relation to avoid overlap with A or B (up to 2 passes)
|
|
for (let i = 0; i < 2; i++) {
|
|
const relBounds = editor.getShapePageBounds(relationId);
|
|
if (!relBounds) break;
|
|
const overlapsA = aBounds && !(relBounds.x > aBounds.x + aBounds.w || relBounds.x + relBounds.w < aBounds.x || relBounds.y > aBounds.y + aBounds.h || relBounds.y + relBounds.h < aBounds.y);
|
|
const overlapsB = bBounds && !(relBounds.x > bBounds.x + bBounds.w || relBounds.x + relBounds.w < bBounds.x || relBounds.y > bBounds.y + bBounds.h || relBounds.y + relBounds.h < bBounds.y);
|
|
if (overlapsA || overlapsB) {
|
|
baseOffset = Math.min(200, Math.floor(baseOffset * 1.5));
|
|
pos = { x: (acx + bcx) / 2 + px * baseOffset, y: (acy + bcy) / 2 + py * baseOffset };
|
|
editor.updateShape({ id: relationId, type: "ai-text-result", x: pos.x, y: pos.y });
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
const kg = new KnowledgeGraphManager(editor);
|
|
// Both arrows point into the relation shape: a -> relation, b -> relation
|
|
kg.createConnection(aId, relationId, "relates_to");
|
|
kg.createConnection(bId, relationId, "relates_to");
|
|
});
|
|
return relationId;
|
|
}
|