Skip to content

Frame the parts a plan's groups name - #10

Merged
XDGFX merged 1 commit into
mainfrom
group-frames
Oct 13, 2026
Merged

XDGFX merged 1 commit into
mainfrom
group-frames

Conversation

@XDGFX

@XDGFX XDGFX commented Oct 13, 2026

Copy link
Copy Markdown
Owner

Stacked on #9 (coil-pipes).

Summary

A plumbing plan, or a prepended file, can frame parts that sit together, such as everything in one enclosure.

groups:
  water_box:
    label: WATER BOX                      # optional title, top left
    members: [pump, accumulator, tee.t1]  # parts as a chain names them
 root (ELK layered)
+  elk.hierarchyHandling: INCLUDE_CHILDREN      # only when the sheet has groups
   card, card, ...
+  frame:water_box                              # compound node, padded for its title
+    member cards
+    turn node, when both its ends are members
   turn nodes
  • The frame holds its members and no other card; pipes cross the border. It is drawn first, beneath cards and pipes, dashed in the theme's faint colour.
  • A fresh instance (adapter.) cannot be a member. A fitting between two members that is not listed is drawn outside the frame, and its pipes go the long way round; the docs say to name it and list it.
  • A member no chain names is left out; a group with none drawn is an error, as are an undeclared component, a pipe, a part listed twice or in two groups, and any key but label and members.
  • diagram.wrap works: a frame is kept whole on one row.
  • elkjs throws ordering a nested graph by model order, so parts inside a frame are ordered by ELK alone. A plan may lay out differently once it has a group.

Evidence

  • Before: no way to show which parts share an enclosure.
    After: a real heating plan with a nine-member water_box group renders a frame tight round those nine cards, with the heater and the second heat exchanger outside and no pipe through a card. One unlisted adapter between two members sends a hose round the frame; naming and listing it gives a clean sheet. Both were rendered and checked by eye, as were frames with wrap, a turn inside a frame, between two frames, and from a frame to a bare card.
  • Sheets without groups are byte-identical: all 14 SVGs and 10 Markdown tables of the project that uses ferrule were rendered from this branch and compared with cmp against the committed ones, 0 differ.
  • npm run typecheck, npm test (93 pass, 8 new in test/groups.test.ts) and npm run build pass.
a frame encloses its members, under a band for its title, and no other card
a frame is at least as wide as its title
a turn stays inside a frame when both its ends do, and outside every frame otherwise
a sheet with frames still wraps into rows, each frame kept whole
a frame is drawn first, beneath cards and pipes, and only a sheet with groups carries its style
rejects a group that names what it can't frame

Merge Danger

Door: two-way

Opt-in key; reverting removes it and nothing else.

Blast Radius: none-unless-opted-in

A plan that already has a stray top-level groups key is now read, and fails if it is not in this shape. Groups are plumbing only; a loom's groups is still ignored.

🤖 Generated with Claude Code

A top-level `groups` map, in the plan or a prepended file, draws a dashed
rounded frame with a small title round each group's members:

    groups:
      water_box:
        label: WATER BOX
        members: [pump, accumulator, tee.t1]

A member is a part as a chain names it, or a named instance. Each group is an
ELK compound node laid out with INCLUDE_CHILDREN, so the frame holds its
members and no other card, and pipes cross its border to reach them. A turn
whose two ends are in one group sits inside it; any other turn sits outside
every frame. Wrapping keeps a frame whole on one row. The frame is drawn
first, beneath cards and pipes, and its CSS rule is only written on a sheet
that has groups, so a sheet without them renders to the same bytes as before.

elkjs throws when it orders a nested graph by model order, so the parts inside
a frame are ordered by ELK alone.

A member that is not a declared component, a pipe, a fresh instance, a part
listed twice or in two groups, a group with no member drawn, and any key but
`label` and `members` are errors. A member no chain names is left out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@XDGFX
XDGFX changed the base branch from coil-pipes to main October 13, 2026 10:45
@XDGFX
XDGFX merged commit d01fe4c into main Oct 13, 2026
1 check passed
@XDGFX
XDGFX deleted the group-frames branch October 13, 2026 10:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant