diff --git a/backend/app/db/database.py b/backend/app/db/database.py index 95c7ff6..653e995 100644 --- a/backend/app/db/database.py +++ b/backend/app/db/database.py @@ -27,6 +27,8 @@ async def init_db() -> None: await conn.exec_driver_sql("ALTER TABLE nodes ADD COLUMN custom_colors JSON") with suppress(Exception): await conn.exec_driver_sql("ALTER TABLE edges ADD COLUMN custom_color TEXT") + with suppress(Exception): + await conn.exec_driver_sql("ALTER TABLE edges ADD COLUMN path_style TEXT") async def get_db(): diff --git a/backend/app/db/models.py b/backend/app/db/models.py index d71c180..4af7884 100644 --- a/backend/app/db/models.py +++ b/backend/app/db/models.py @@ -55,6 +55,7 @@ class Edge(Base): vlan_id: Mapped[int | None] = mapped_column(Integer) speed: Mapped[str | None] = mapped_column(String) custom_color: Mapped[str | None] = mapped_column(String) + path_style: Mapped[str | None] = mapped_column(String) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now) diff --git a/backend/app/schemas/canvas.py b/backend/app/schemas/canvas.py index e105475..1e8c614 100644 --- a/backend/app/schemas/canvas.py +++ b/backend/app/schemas/canvas.py @@ -35,6 +35,7 @@ class EdgeSave(BaseModel): vlan_id: int | None = None speed: str | None = None custom_color: str | None = None + path_style: str | None = None class CanvasSaveRequest(BaseModel): diff --git a/backend/app/schemas/edges.py b/backend/app/schemas/edges.py index 15b3a7c..b2b1435 100644 --- a/backend/app/schemas/edges.py +++ b/backend/app/schemas/edges.py @@ -11,6 +11,7 @@ class EdgeBase(BaseModel): vlan_id: int | None = None speed: str | None = None custom_color: str | None = None + path_style: str | None = None class EdgeCreate(EdgeBase): @@ -23,6 +24,7 @@ class EdgeUpdate(BaseModel): vlan_id: int | None = None speed: str | None = None custom_color: str | None = None + path_style: str | None = None class EdgeResponse(EdgeBase): diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 99c6e16..4cdb03d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -65,6 +65,7 @@ export default function App() { vlan_id: e.data?.vlan_id ?? null, speed: e.data?.speed ?? null, custom_color: e.data?.custom_color ?? null, + path_style: e.data?.path_style ?? null, })) await canvasApi.save({ nodes: nodesToSave, edges: edgesToSave, viewport: {} }) markSaved() diff --git a/frontend/src/components/canvas/edges/index.tsx b/frontend/src/components/canvas/edges/index.tsx index e733532..2a934ef 100644 --- a/frontend/src/components/canvas/edges/index.tsx +++ b/frontend/src/components/canvas/edges/index.tsx @@ -2,6 +2,7 @@ import { BaseEdge, EdgeLabelRenderer, getBezierPath, + getSmoothStepPath, type EdgeProps, type Edge, } from '@xyflow/react' @@ -23,7 +24,10 @@ const EDGE_STYLES: Record = { } export function HomelableEdge({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, data, selected }: EdgeProps>) { - const [edgePath, labelX, labelY] = getBezierPath({ sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition }) + const pathArgs = { sourceX, sourceY, sourcePosition, targetX, targetY, targetPosition } + const [edgePath, labelX, labelY] = data?.path_style === 'smooth' + ? getSmoothStepPath({ ...pathArgs, borderRadius: 8 }) + : getBezierPath(pathArgs) const edgeType: EdgeType = data?.type ?? 'ethernet' const customColor = data?.custom_color as string | undefined diff --git a/frontend/src/components/modals/EdgeModal.tsx b/frontend/src/components/modals/EdgeModal.tsx index 6db9b12..828d740 100644 --- a/frontend/src/components/modals/EdgeModal.tsx +++ b/frontend/src/components/modals/EdgeModal.tsx @@ -5,7 +5,7 @@ import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select' -import { EDGE_TYPE_LABELS, type EdgeData, type EdgeType } from '@/types' +import { EDGE_TYPE_LABELS, type EdgeData, type EdgePathStyle, type EdgeType } from '@/types' import { EDGE_DEFAULT_COLORS } from '@/utils/edgeColors' const EDGE_TYPES = Object.entries(EDGE_TYPE_LABELS) as [EdgeType, string][] @@ -24,6 +24,7 @@ export function EdgeModal({ open, onClose, onSubmit, onDelete, initial, title = const [label, setLabel] = useState(initial?.label ?? '') const [vlanId, setVlanId] = useState(initial?.vlan_id?.toString() ?? '') const [customColor, setCustomColor] = useState(initial?.custom_color) + const [pathStyle, setPathStyle] = useState(initial?.path_style ?? 'bezier') const effectiveColor = customColor ?? EDGE_DEFAULT_COLORS[type] @@ -34,6 +35,7 @@ export function EdgeModal({ open, onClose, onSubmit, onDelete, initial, title = label: label || undefined, vlan_id: type === 'vlan' && vlanId ? parseInt(vlanId) : undefined, custom_color: customColor, + path_style: pathStyle, }) onClose() } @@ -90,6 +92,27 @@ export function EdgeModal({ open, onClose, onSubmit, onDelete, initial, title = /> +
+ +
+ {(['bezier', 'smooth'] as EdgePathStyle[]).map((style) => ( + + ))} +
+
+
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index fc4359a..e7169f6 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -44,12 +44,15 @@ export interface NodeData extends Record { custom_colors?: { border?: string; background?: string; icon?: string } } +export type EdgePathStyle = 'bezier' | 'smooth' + export interface EdgeData extends Record { type: EdgeType label?: string vlan_id?: number speed?: string custom_color?: string + path_style?: EdgePathStyle } export const NODE_TYPE_LABELS: Record = {