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:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user