Add formboard harness layout designer
Separate read-only canvas for physically laying out wire harnesses.
Syncs connectors from the main diagram (one-way), no changes flow back.
Backend:
- FormboardLayout, FormboardNode, FormboardSegment models
- /api/formboard/ router: get/create layout, sync connectors from diagram,
full CRUD for nodes and segments
Frontend (formboard.js):
- Konva canvas: drag nodes, click-to-connect segments, zoom/pan
- Node types: Connector (blue rect, synced from diagram), Branch (green
circle, manual), Splice/joint (orange diamond, manual)
- Segments scale thickness by wire count; fitting overlays for open,
split loom, conduit, heat shrink, tape wrap
- Right-click context menu for delete on nodes/segments
App integration:
- "⊞ Formboard" tab in view-tab-bar (distinct color, independent of views)
- Sub-toolbar: Select / Branch / Splice / Connect / Sync / Fit / Delete
- Right panel swaps to formboard props when active:
- Node: label, notes, linked device info
- Segment: fitting type, fitting color, label, length, wire checklist
(checkboxes for every wire in the main diagram)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -124,6 +124,45 @@ class ViewWireWaypoints(Base):
|
||||
view = relationship("DiagramView", back_populates="wire_waypoints")
|
||||
|
||||
|
||||
class FormboardLayout(Base):
|
||||
__tablename__ = "formboard_layouts"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
diagram_id = Column(Integer, ForeignKey("diagrams.id", ondelete="CASCADE"), nullable=False, unique=True)
|
||||
|
||||
nodes = relationship("FormboardNode", back_populates="layout", cascade="all, delete-orphan")
|
||||
segments = relationship("FormboardSegment", back_populates="layout", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class FormboardNode(Base):
|
||||
__tablename__ = "formboard_nodes"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
layout_id = Column(Integer, ForeignKey("formboard_layouts.id", ondelete="CASCADE"), nullable=False)
|
||||
node_type = Column(String, default="branch") # connector | branch | splice
|
||||
device_id = Column(Integer, ForeignKey("devices.id", ondelete="SET NULL"), nullable=True)
|
||||
x = Column(Float, default=100)
|
||||
y = Column(Float, default=100)
|
||||
label = Column(String, default="")
|
||||
notes = Column(Text, default="")
|
||||
|
||||
layout = relationship("FormboardLayout", back_populates="nodes")
|
||||
|
||||
|
||||
class FormboardSegment(Base):
|
||||
__tablename__ = "formboard_segments"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
layout_id = Column(Integer, ForeignKey("formboard_layouts.id", ondelete="CASCADE"), nullable=False)
|
||||
from_node_id = Column(Integer, ForeignKey("formboard_nodes.id", ondelete="CASCADE"), nullable=False)
|
||||
to_node_id = Column(Integer, ForeignKey("formboard_nodes.id", ondelete="CASCADE"), nullable=False)
|
||||
waypoints = Column(JSON, default=[])
|
||||
wire_ids = Column(JSON, default=[]) # wire IDs from main diagram
|
||||
fitting_type = Column(String, default="open") # open | loom | conduit | heat_shrink | tape
|
||||
fitting_color = Column(String, default="#888888")
|
||||
label = Column(String, default="")
|
||||
length_mm = Column(Float, nullable=True)
|
||||
|
||||
layout = relationship("FormboardLayout", back_populates="segments")
|
||||
|
||||
|
||||
class CustomConnector(Base):
|
||||
__tablename__ = "custom_connectors"
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
|
||||
Reference in New Issue
Block a user