feat: enhance canvas styling and edge handling with glow effects

- Added custom CSS variables for edge stroke and connection line styles in light and dark modes to improve visual consistency.
- Updated edge rendering logic to utilize new styles, enhancing the appearance of temporary edges during interactions.
- Introduced a new utility function for applying glow effects based on source node types, improving the visual feedback of connections.
- Refactored edge mapping to support glow effects, enhancing user experience during node interactions.
This commit is contained in:
Matthias
2026-03-28 11:02:19 +01:00
parent b243443431
commit 4b4d784768
3 changed files with 92 additions and 5 deletions

View File

@@ -145,6 +145,23 @@
}
@layer components {
/* Kanten: doppelte Standard-Dicke, feste Kontrastfarben je Light/Dark (gleiches Schema wie colorMode) */
.react-flow {
--xy-edge-stroke: rgba(44, 62, 80, 1);
--xy-edge-stroke-width: 2;
--xy-edge-stroke-selected: rgba(44, 62, 80, 1);
--xy-connectionline-stroke: rgba(44, 62, 80, 1);
--xy-connectionline-stroke-width: 1;
}
.react-flow.dark {
--xy-edge-stroke: rgba(189, 195, 199, 1);
--xy-edge-stroke-width: 2;
--xy-edge-stroke-selected: rgba(189, 195, 199, 1);
--xy-connectionline-stroke: rgba(189, 195, 199, 1);
--xy-connectionline-stroke-width: 1;
}
/* Verbindungs-Punkte über Node-Inhalt (XYFlow setzt kein z-index) */
.react-flow__handle {
z-index: 50;
@@ -156,9 +173,16 @@
}
.react-flow__edge.temp .react-flow__edge-path {
stroke: hsl(var(--foreground) / 0.3);
stroke-width: 2.5;
stroke-width: 5;
stroke-dasharray: 8 6;
stroke-linecap: round;
}
.react-flow:not(.dark) .react-flow__edge.temp .react-flow__edge-path {
stroke: rgba(44, 62, 80, 0.35);
}
.react-flow.dark .react-flow__edge.temp .react-flow__edge-path {
stroke: rgba(189, 195, 199, 0.35);
}
}

View File

@@ -34,6 +34,7 @@ import {
convexNodeDocWithMergedStorageUrl,
convexNodeToRF,
convexEdgeToRF,
convexEdgeToRFWithSourceGlow,
NODE_DEFAULTS,
NODE_HANDLE_MAP,
resolveMediaAspectRatio,
@@ -133,7 +134,7 @@ const DEFAULT_EDGE_OPTIONS: DefaultEdgeOptions = {
};
const EDGE_INTERSECTION_HIGHLIGHT_STYLE: NonNullable<RFEdge["style"]> = {
stroke: "hsl(var(--foreground))",
stroke: "var(--xy-edge-stroke)",
strokeWidth: 2,
};
@@ -622,10 +623,23 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
if (!convexEdges) return;
setEdges((prev) => {
const tempEdges = prev.filter((e) => e.className === "temp");
const mapped = convexEdges.map(convexEdgeToRF);
const sourceTypeByNodeId =
convexNodes !== undefined
? new Map(convexNodes.map((n) => [n._id, n.type]))
: undefined;
const glowMode = resolvedTheme === "dark" ? "dark" : "light";
const mapped = convexEdges.map((edge) =>
sourceTypeByNodeId
? convexEdgeToRFWithSourceGlow(
edge,
sourceTypeByNodeId.get(edge.sourceNodeId),
glowMode,
)
: convexEdgeToRF(edge),
);
return [...mapped, ...tempEdges];
});
}, [convexEdges]);
}, [convexEdges, convexNodes, resolvedTheme]);
useEffect(() => {
if (isDragging.current) return;
@@ -1278,6 +1292,7 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
<ReactFlow
nodes={nodes}
edges={edges}
onlyRenderVisibleElements
defaultEdgeOptions={DEFAULT_EDGE_OPTIONS}
nodeTypes={nodeTypes}
onNodesChange={onNodesChange}

View File

@@ -84,6 +84,54 @@ export function convexEdgeToRF(edge: Doc<"edges">): RFEdge {
};
}
/**
* Akzentfarben der Handles je Node-Typ (s. jeweilige Node-Komponente).
* Für einen dezenten Glow entlang der Kante (drop-shadow am Pfad).
*/
const SOURCE_NODE_GLOW_RGB: Record<string, readonly [number, number, number]> = {
prompt: [139, 92, 246],
"ai-image": [139, 92, 246],
image: [13, 148, 136],
text: [13, 148, 136],
note: [13, 148, 136],
asset: [13, 148, 136],
group: [100, 116, 139],
frame: [249, 115, 22],
compare: [100, 116, 139],
};
export type EdgeGlowColorMode = "light" | "dark";
function sourceGlowFilterForNodeType(
type: string | undefined,
colorMode: EdgeGlowColorMode,
): string | undefined {
if (!type) return undefined;
const rgb = SOURCE_NODE_GLOW_RGB[type];
if (!rgb) return undefined;
const [r, g, b] = rgb;
if (colorMode === "dark") {
/* Zwei kleine Schatten statt gestapelter großer Blur — weniger GPU-Last beim Pan/Zoom */
return `drop-shadow(0 0 4px rgba(${r},${g},${b},0.72)) drop-shadow(0 0 9px rgba(${r},${g},${b},0.38))`;
}
return `drop-shadow(0 0 3px rgba(${r},${g},${b},0.42)) drop-shadow(0 0 7px rgba(${r},${g},${b},0.2))`;
}
/** Wie convexEdgeToRF, setzt zusätzlich filter am Pfad nach Quell-Node-Typ. */
export function convexEdgeToRFWithSourceGlow(
edge: Doc<"edges">,
sourceNodeType: string | undefined,
colorMode: EdgeGlowColorMode = "light",
): RFEdge {
const base = convexEdgeToRF(edge);
const filter = sourceGlowFilterForNodeType(sourceNodeType, colorMode);
if (!filter) return base;
return {
...base,
style: { ...(base.style ?? {}), filter },
};
}
/**
* Handle-IDs pro Node-Typ für Proximity Connect.
* `undefined` = default handle (kein explizites `id`-Attribut auf dem Handle).