feat: selectable marker shapes per edge endpoint
Replace the on/off arrowhead toggle with a per-end shape picker. Each end (start/end) independently selects: none, arrow, arrow-open, circle, diamond, or square. Markers still recolor live from the resolved stroke color. Frontend: - MarkerShape type + edgeMarkers util (normalizeMarker, MARKER_GEOMETRY); legacy boolean coerces to 'arrow' on read. - Per-shape <marker> inner geometry; symmetric shapes use fixed orient. - MarkerShapePicker reused in EdgeModal and CustomStyleModal. - Serializer normalizes to shape strings. Backend: - Edge marker columns Boolean -> String (default 'none'); TEXT migration. - normalize_marker() + validators coerce legacy bool / unknown values. ha-relevant: yes
This commit is contained in:
@@ -51,26 +51,37 @@ async def test_save_canvas_creates_nodes_and_edges(client: AsyncClient, headers:
|
||||
assert canvas["viewport"] == {"x": 1, "y": 2, "zoom": 1.5}
|
||||
|
||||
|
||||
async def test_save_canvas_round_trips_arrow_markers(client: AsyncClient, headers: dict):
|
||||
async def test_save_canvas_round_trips_marker_shapes(client: AsyncClient, headers: dict):
|
||||
n1 = node_payload(label="Router", type="router")
|
||||
n2 = node_payload(label="Switch", type="switch")
|
||||
e1 = edge_payload(n1["id"], n2["id"], marker_start=True, marker_end=True)
|
||||
e1 = edge_payload(n1["id"], n2["id"], marker_start="diamond", marker_end="arrow")
|
||||
await client.post("/api/v1/canvas/save", json={"nodes": [n1, n2], "edges": [e1], "viewport": {}}, headers=headers)
|
||||
|
||||
edge = (await client.get("/api/v1/canvas", headers=headers)).json()["edges"][0]
|
||||
assert edge["marker_start"] is True
|
||||
assert edge["marker_end"] is True
|
||||
assert edge["marker_start"] == "diamond"
|
||||
assert edge["marker_end"] == "arrow"
|
||||
|
||||
|
||||
async def test_save_canvas_defaults_arrow_markers_off(client: AsyncClient, headers: dict):
|
||||
async def test_save_canvas_coerces_legacy_boolean_marker(client: AsyncClient, headers: dict):
|
||||
n1 = node_payload(label="Router", type="router")
|
||||
n2 = node_payload(label="Switch", type="switch")
|
||||
e1 = edge_payload(n1["id"], n2["id"], marker_start=True, marker_end=False)
|
||||
await client.post("/api/v1/canvas/save", json={"nodes": [n1, n2], "edges": [e1], "viewport": {}}, headers=headers)
|
||||
|
||||
edge = (await client.get("/api/v1/canvas", headers=headers)).json()["edges"][0]
|
||||
assert edge["marker_start"] == "arrow"
|
||||
assert edge["marker_end"] == "none"
|
||||
|
||||
|
||||
async def test_save_canvas_defaults_markers_none(client: AsyncClient, headers: dict):
|
||||
n1 = node_payload(label="Router", type="router")
|
||||
n2 = node_payload(label="Switch", type="switch")
|
||||
e1 = edge_payload(n1["id"], n2["id"])
|
||||
await client.post("/api/v1/canvas/save", json={"nodes": [n1, n2], "edges": [e1], "viewport": {}}, headers=headers)
|
||||
|
||||
edge = (await client.get("/api/v1/canvas", headers=headers)).json()["edges"][0]
|
||||
assert edge["marker_start"] is False
|
||||
assert edge["marker_end"] is False
|
||||
assert edge["marker_start"] == "none"
|
||||
assert edge["marker_end"] == "none"
|
||||
|
||||
|
||||
async def test_save_canvas_round_trips_per_side_handles(client: AsyncClient, headers: dict):
|
||||
|
||||
Reference in New Issue
Block a user