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 {
|
@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) */
|
/* Verbindungs-Punkte über Node-Inhalt (XYFlow setzt kein z-index) */
|
||||||
.react-flow__handle {
|
.react-flow__handle {
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
@@ -156,9 +173,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__edge.temp .react-flow__edge-path {
|
.react-flow__edge.temp .react-flow__edge-path {
|
||||||
stroke: hsl(var(--foreground) / 0.3);
|
stroke-width: 5;
|
||||||
stroke-width: 2.5;
|
|
||||||
stroke-dasharray: 8 6;
|
stroke-dasharray: 8 6;
|
||||||
stroke-linecap: round;
|
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,
|
convexNodeDocWithMergedStorageUrl,
|
||||||
convexNodeToRF,
|
convexNodeToRF,
|
||||||
convexEdgeToRF,
|
convexEdgeToRF,
|
||||||
|
convexEdgeToRFWithSourceGlow,
|
||||||
NODE_DEFAULTS,
|
NODE_DEFAULTS,
|
||||||
NODE_HANDLE_MAP,
|
NODE_HANDLE_MAP,
|
||||||
resolveMediaAspectRatio,
|
resolveMediaAspectRatio,
|
||||||
@@ -133,7 +134,7 @@ const DEFAULT_EDGE_OPTIONS: DefaultEdgeOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const EDGE_INTERSECTION_HIGHLIGHT_STYLE: NonNullable<RFEdge["style"]> = {
|
const EDGE_INTERSECTION_HIGHLIGHT_STYLE: NonNullable<RFEdge["style"]> = {
|
||||||
stroke: "hsl(var(--foreground))",
|
stroke: "var(--xy-edge-stroke)",
|
||||||
strokeWidth: 2,
|
strokeWidth: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -622,10 +623,23 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
|||||||
if (!convexEdges) return;
|
if (!convexEdges) return;
|
||||||
setEdges((prev) => {
|
setEdges((prev) => {
|
||||||
const tempEdges = prev.filter((e) => e.className === "temp");
|
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];
|
return [...mapped, ...tempEdges];
|
||||||
});
|
});
|
||||||
}, [convexEdges]);
|
}, [convexEdges, convexNodes, resolvedTheme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isDragging.current) return;
|
if (isDragging.current) return;
|
||||||
@@ -1278,6 +1292,7 @@ function CanvasInner({ canvasId }: CanvasInnerProps) {
|
|||||||
<ReactFlow
|
<ReactFlow
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
|
onlyRenderVisibleElements
|
||||||
defaultEdgeOptions={DEFAULT_EDGE_OPTIONS}
|
defaultEdgeOptions={DEFAULT_EDGE_OPTIONS}
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
onNodesChange={onNodesChange}
|
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.
|
* Handle-IDs pro Node-Typ für Proximity Connect.
|
||||||
* `undefined` = default handle (kein explizites `id`-Attribut auf dem Handle).
|
* `undefined` = default handle (kein explizites `id`-Attribut auf dem Handle).
|
||||||
|
|||||||
Reference in New Issue
Block a user