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:
Pouzor
2026-07-05 11:30:49 +02:00
parent 1cf525844b
commit c95d104245
20 changed files with 385 additions and 157 deletions
+15 -8
View File
@@ -156,6 +156,13 @@ export interface NodeData extends Record<string, unknown> {
export type EdgePathStyle = 'bezier' | 'smooth'
/**
* Endpoint marker shape for an edge end. `none` = no marker.
* Legacy saves stored a boolean (`true` = filled arrow) — coerced via
* `normalizeMarker` in utils/edgeMarkers.
*/
export type MarkerShape = 'none' | 'arrow' | 'arrow-open' | 'circle' | 'diamond' | 'square'
export interface Waypoint {
x: number
y: number
@@ -169,10 +176,10 @@ export interface EdgeData extends Record<string, unknown> {
custom_color?: string
path_style?: EdgePathStyle
animated?: boolean | 'snake' | 'flow' | 'basic' | 'none'
/** Filled arrowhead at the source end. */
marker_start?: boolean
/** Filled arrowhead at the target end. */
marker_end?: boolean
/** Marker shape at the source end. Legacy boolean (`true`=arrow) coerced on read. */
marker_start?: MarkerShape | boolean
/** Marker shape at the target end. Legacy boolean (`true`=arrow) coerced on read. */
marker_end?: MarkerShape | boolean
waypoints?: Waypoint[]
}
@@ -261,10 +268,10 @@ export interface EdgeTypeStyle {
opacity: number
pathStyle: EdgePathStyle
animated: 'none' | 'snake' | 'flow' | 'basic'
/** Default filled arrowhead at the source end for new edges of this type. */
arrowStart: boolean
/** Default filled arrowhead at the target end for new edges of this type. */
arrowEnd: boolean
/** Default marker shape at the source end for new edges of this type. */
arrowStart: MarkerShape
/** Default marker shape at the target end for new edges of this type. */
arrowEnd: MarkerShape
}
export interface CustomStyleDef {