Repository navigation
feat: a group can be marked as the one the flow starts in - #173
Merged
Merged
Conversation
An edge whose end names a group's scope ends on the group's frame instead of at one of its members: a transition into a state, where the state is a group of what can happen in it. The transform resolves a scope id as an end; `splitEdgesAtGroups` gives every such edge one place on the frame (`GROUP_END`, left for edges ending there, right for edges starting there), shared by all of them, crossing the frames around the group as any edge does. The client keeps the arrowhead of an edge that ends there and draws no dot. An edge between a group and something inside it (or itself) goes nowhere the frame does not already hold. It is taken out of the model and the group is marked (`GROUP_LOOP`): one loop on the frame for all of them. A group whose label is empty has no title and no header band; only a scope with no label at all is named by its id. A scope's label now reaches the document on the sidecar-export path, which dropped it. Reopening a saved layout puts the place where edges end back where the edge outside the group reaches it, there being no piece inside to go by. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
A producer puts the members of a group in rows with `meta.row` (a whole number from zero, the first row on top). The members of a row sit side by side in the order the producer gave them, the rows one under another; a member with no row comes after the last. The layers of a layout that runs downward are rows, and a member's row is its partition: such a group is laid out with `elk.direction: DOWN`, partitioning on, and its members as one component (on their own, members with nothing between them would each be a component, packed wherever it fits). The engine forces every nested layout to the right; a group in rows is the exception. The pass that pins sources and sinks to the outer layers when a diagram opens leaves a row's members alone: a layer constraint would contradict their row. It also no longer pins a node that has no port at all, which is neither a source nor a sink. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
Reopening a saved layout: a port where edges end at a group has no member to be level with, and the layout may put it below the members. The frame was rebuilt around the members alone, so such a port was clamped to the new bottom edge and its edges turned diagonal. The frame now reaches down past every port its saved routes meet. A real lay out, save and reopen of such a document is tested. The selection trace took the port where edges end at a group for one more port to pass through, with nothing behind it: the group was never on a path. That port is the group's own; the group is on the path, and what it holds is left as it is instead of faded. A group that holds nothing was laid out 20 by 20, its title running over its edges. Groups have a minimum size, on layout and on reopening. The loop of a group keeps what its edges said: their captions and error messages are its tooltip, and it is drawn in the error colour when one of them is in error. Before, they went with the edges. Where an edge starts at a group the dot stays (the line began 4 px off the frame without it); only where edges arrive does the arrowhead replace it. The property panel names the group an edge ends at by its title. Tests: the loop from a group to a member and to itself, sharing on the way out, group to group, an empty group's size, the trace through a group. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
A producer pins a group with `pin: 'top'` on its scope: what belongs to the whole picture rather than to a place in its flow, as a legend does, or the variables a state machine works on. Laid out with the rest, such a group is one more node of the layered layout: it takes a layer, and its width moves everything in it. Pinned groups are taken out of the graph the layout sees, laid out apart (their inside, and so their size), then stacked in a band above what the layout made: the first on top, left edges on the diagram's. The rest ends up exactly where it would without them. A pinned group that an edge crosses or ends at stays in the flow, since its edges need it there. Routes of edges inside a pinned group move with it. The scope's `pin` reaches the document from a sidecar's export. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
Some relations are too many to draw all at once: every state variable an action reads, every action that takes a token from a port. A producer marks such an edge `meta.onSelection`. The transform sets it aside: it is no edge of the model, takes no part in the layout and crosses no group frame (so a pinned group it touches stays pinned). It reaches the client as data on the root (`SELECTION_LINKS`), its ends resolved to the model's nodes: a node by its id, a port by its node, a group scope by its group. The client's root view draws the links of the selection over the diagram (selection-links.ts): every link the selected node is an end of, the node at its other end outlined, the diagram's own edges faded behind. A link leaves a node by the side that faces the other end, or by the side across when a neighbour of the node's own frame is in the way there, so it runs beside a frame's rows and not over them; links sharing a side are spread along it in the order of where they lead, so none crosses another at the node. `meta.role` is the class `selection-link-<role>` a product colours, `meta.label` a word on the link (hidden when zoomed out), `meta.bidirectional` an arrowhead at each end. A node's `meta.sameAs` names what it is one drawing of. Selecting it outlines the other nodes that draw the same thing. The links run straight over whatever lies between their ends; routing them around other frames is not done. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
Rows are for members that relate only to what is outside their group. With an edge between two members the layering puts its ends in different layers, which split a row, and keeping the producer's order within a row sent the edge through the members between its ends. A group with such an edge now keeps the plain layout, laid out by its edges as before. Which groups lay out in rows is decided once the groups are nested and the edges cut (`arrangeGroupRows`), not while each group is built. A group nested in a rows group is a member with a row of its own (a scope's `row`), or after the last; a member opened in place keeps its row on the frame it becomes. Before, both landed in the top row. A group laid out in rows says so with `GROUP_ROWS`; `GROUP_ROW` is the row of a member only. A row past what a layout can count is no row. The changelog entry had been merged into the middle of its neighbour. Left for later: a short row is not left-aligned over a long one, and a frame grows by a track for every member's leaving edge. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
A producer puts lines of text in a node with meta.lines: what a reader zooms in for, such as the condition a step waits on or what it takes and gives. Each line is a band across the bottom of the node's body, which is taller by them and as wide as the longest. The band is always drawn and carries node-line-<kind> for a product to colour; its text is detail, drawn only close in. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
A producer marks a group with start: true on its scope: the initial state of a state machine, the first stage of a pipeline. Its title gets a filled dot before it, as a statechart marks its initial state. A group with no title has no band for it and draws none. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
… group is not pinned From the review of #170. The band was placed from the root's node boxes alone. The layout runs an edge that goes back over the top row, and the band sat on it. Its bottom is now a gap above the highest node or route point, whichever is higher. pin on a group nested in another was passed to the client and then ignored by the layout. It is now dropped where the group is nested. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
…rossing, and end where a node is drawn From the review of #171. - The trace faded the nodes the selection's links lead to, and the links' dimming hid the trace. What the links lead to, and the other drawings of what is selected, are now left unfaded by the trace; an edge on the trace, selected or in error is not dimmed. - Links sharing a side were ordered by where they lead across it, which ties for a row of nodes seen from a side, and then by id: the fan crossed itself. They are ordered by the direction they lead in; two links between the same two nodes run side by side. - A link's end the platform draws as something else was lost: the links are taken out before the document is rewritten and end at the frame of a node opened in place, or at the bus a port of the unit is folded into. No bus counts a link among its wires. A link between a frame and what is in it is dropped. - Each side is sorted once, a node's neighbours found once, and a model with no link and no sameAs is not walked on every frame. - The edges are dimmed only when a link is drawn, not for outlines alone. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
The merge of main left the entry's earlier wording beside the new one. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
…and rows stand on their tops From the review of #172. - The cut at 48 fell between the two halves of a character outside the basic plane, and the headless render died on the lone half. The cut is now by character and by column: a wide character (East Asian, an emoji) takes two, in the estimate of the node's width as well. - Control characters reached the exported SVG raw and made it no XML; they, the marks that turn the direction of the text, and the spaces of no width are taken out of a line. - An if or a loop took lines on the server, which its own view never drew: it takes none. - The members of a row of a group stand on one line along their tops: a node with lines beside one without was centred on it. - The parser sits with the other meta parsers. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
… arrow, title or no title From the review of #173. - The scope's field is initial and the argument GROUP_INITIAL: start read as the pair of GROUP_END, which is where an edge meets a frame, and as a place the layout would put the group in. It does neither. - The mark is a dot and a short arrow, as a statechart draws it: a dot alone was the glyph of a port. - A group with no title has the mark alone, in the room every frame keeps for a header; it was dropped without a word. - The test renders the group's view and checks what is drawn and where, where it read the view's source. Claude-Session: https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The state machine of the actor view had no way to say which state is the initial one. (#168 to #172 are merged; the base is
main.)What it does
A producer marks a group with
initial: trueon its scope: the initial state of a state machine, the first stage of a pipeline. The group's frame gets a filled dot and a short arrow before its title, as a statechart marks its initial state. A group with no title has the mark alone, in the room every frame keeps for a header. The mark is inside the frame, so the layout is not touched, and it says what the group is, not where it goes.Where
shared:WorkflowDiagramMetadata.GROUP_INITIAL(wf:groupInitial).diagram-server:PyGraphScope.initial, the group's argument. Only a plaintruecounts.sidecar-toolkitgraph-payload-to-doc.ts: passesinitialthrough, only a true one.diagram-client:group-initial-mark.ts, drawn byGroupNodeView; a product colours it with.workflow-node-group .group-initial-mark { color: … }.Checked
group-scopes), the toolkit (graph-payload-scope-label) and the client (group-initial-mark, which renders the group's view: the mark's place with a title, without one, after an open node's toggle, and not for anything buttrue).npm run typecheck,npm run check:neutrality,npm test: all pass.After review
An independent review rendered eleven documents (titled, untitled, long title, two marked groups, nested, empty, pinned, with a loop, rows, a cycle, light theme) and found nothing to fix before merge. Taken from it:
start/GROUP_START: it read as the pair ofGROUP_END(where an edge meets a frame) and, besidepinandrow, as a placement.Limits
https://claude.ai/code/session_01EToj6RJRMSSHQiqGNMU9wv