feat: implement Story 2.8 — Flowchart diagram type renderer
Add the 6th and final core diagram type: flowcharts with standard ISO 5807 shapes (process, decision, terminal, I/O, subprocess), orthogonal edge routing with decision outcome labels, and ELK layered auto-layout. Code review fixes included: decision diamond ELK height corrected (80→130px), icon rendering made conditional, data.color border override added to all nodes, ELK sizing ternary refactored to getNodeDimensions() helper, constants unified to lookup maps. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,876 @@
|
||||
# Story 2.8: Flowchart Diagram Type Renderer
|
||||
|
||||
Status: done
|
||||
|
||||
<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
||||
|
||||
## Story
|
||||
|
||||
As a user,
|
||||
I want to create and view flowcharts with decision nodes, process steps, and terminals,
|
||||
so that I can model logic flows and decision processes.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. **Given** I open or create a flowchart, **When** the canvas renders, **Then** I see standard flowchart shapes: process (rectangle), decision (diamond), start/end terminals (rounded rectangle/stadium), I/O (parallelogram), subprocess (double-bordered rectangle), **And** each node displays its label text.
|
||||
|
||||
2. **Given** a flowchart has decision nodes, **When** rendered, **Then** decision diamonds show the question/condition text, **And** outgoing edges are labeled with the decision outcomes (Yes/No, True/False, or custom).
|
||||
|
||||
3. **Given** auto-layout runs on a flowchart, **When** the diagram has branching paths, **Then** ELK.js produces a clean top-down flow with branches clearly separated, **And** merge points are visually clear.
|
||||
|
||||
## Tasks / Subtasks
|
||||
|
||||
- [x] Task 1: Create flowchart constants and type registry (AC: #1, #2)
|
||||
- [x] 1.1: Create `apps/web/src/modules/diagram/types/flowchart/constants.ts` with `FLOW_SIZES`, `resolveFlowchartNodeType()`, `resolveFlowchartEdgeType()`, `getFlowNodeSize()`
|
||||
- [x] 1.2: Define sizes for 5 node subtypes: process (160x60), decision (140x80), terminal (140x50), io (160x60), subprocess (160x60)
|
||||
|
||||
- [x] Task 2: Create FlowProcessNode custom @xyflow/react component (AC: #1)
|
||||
- [x] 2.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowProcessNode.tsx` — standard rectangle with label text, icon support, rose accent border
|
||||
|
||||
- [x] Task 3: Create FlowDecisionNode custom @xyflow/react component (AC: #1, #2)
|
||||
- [x] 3.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowDecisionNode.tsx` — diamond shape via CSS `transform: rotate(45deg)` on a wrapper with counter-rotated content, condition/question text centered
|
||||
|
||||
- [x] Task 4: Create FlowTerminalNode custom @xyflow/react component (AC: #1)
|
||||
- [x] 4.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowTerminalNode.tsx` — rounded rectangle/stadium shape (`border-radius: 999px` sides), label text, used for both start and end nodes. `data.tag` distinguishes "start" vs "end" for optional styling (e.g., start=green tint, end=red tint, or both use accent)
|
||||
|
||||
- [x] Task 5: Create FlowIoNode custom @xyflow/react component (AC: #1)
|
||||
- [x] 5.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowIoNode.tsx` — parallelogram shape via CSS `transform: skewX(-10deg)` on wrapper with counter-skewed content, label text
|
||||
|
||||
- [x] Task 6: Create FlowSubprocessNode custom @xyflow/react component (AC: #1)
|
||||
- [x] 6.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowSubprocessNode.tsx` — double-bordered rectangle (outer border + inner inset border), label text
|
||||
|
||||
- [x] Task 7: Create FlowEdge custom @xyflow/react component (AC: #2, #3)
|
||||
- [x] 7.1: Create `apps/web/src/modules/diagram/types/flowchart/FlowEdge.tsx` — solid arrow using `getSmoothStepPath` (orthogonal routing), label from `edge.label` shown at midpoint (for decision outcomes Yes/No), rose accent color, markerEnd `url(#flow-arrow)`
|
||||
|
||||
- [x] Task 8: Update graph converter for flowchart type resolution (AC: #1, #2)
|
||||
- [x] 8.1: Import `resolveFlowchartNodeType`, `resolveFlowchartEdgeType` from `../types/flowchart/constants` in `graph-converter.ts`
|
||||
- [x] 8.2: Replace `// Future: flowchart` comment (line 50) with: `if (diagramType === "flowchart" || nodeType.startsWith("flow:")) { return resolveFlowchartNodeType(nodeType); }`
|
||||
- [x] 8.3: Add flowchart diagramType handling in `resolveFlowEdgeType()` before the default return
|
||||
|
||||
- [x] Task 9: Register flowchart types in DiagramCanvas (AC: #1, #2)
|
||||
- [x] 9.1: Import FlowProcessNode, FlowDecisionNode, FlowTerminalNode, FlowIoNode, FlowSubprocessNode, FlowEdge in `DiagramCanvas.tsx`
|
||||
- [x] 9.2: Add flowchart entries to `nodeTypes` object: `flowProcess`, `flowDecision`, `flowTerminal`, `flowIo`, `flowSubprocess` (OUTSIDE component)
|
||||
- [x] 9.3: Add flowchart entry to `edgeTypes` object: `flowEdge` (OUTSIDE component)
|
||||
- [x] 9.4: Add flowchart arrow marker to `MarkerDefs`: `#flow-arrow` with rose accent color
|
||||
|
||||
- [x] Task 10: Integrate flowchart node sizing in ELK layout (AC: #3)
|
||||
- [x] 10.1: Import `getFlowNodeSize` from `../types/flowchart/constants` in `elk-layout.ts`
|
||||
- [x] 10.2: Add flowchart sizing to the width/height computation chain in `buildElkGraph()` — after `seqSize` check, add `flowSize` check
|
||||
|
||||
- [x] Task 11: Add flowchart CSS styles (AC: #1, #2)
|
||||
- [x] 11.1: Add `.flow-process`, `.flow-decision`, `.flow-decision-diamond`, `.flow-terminal`, `.flow-io`, `.flow-io-skew`, `.flow-subprocess`, `.flow-subprocess-inner` styles to `globals.css`
|
||||
- [x] 11.2: Use `--diagram-flowchart` rose accent color for all flowchart elements (already defined at line 29)
|
||||
|
||||
- [x] Task 12: Tests (AC: all)
|
||||
- [x] 12.1: Unit tests for flowchart constants — `resolveFlowchartNodeType` for all 5 subtypes (prefixed and bare), `resolveFlowchartEdgeType`, `getFlowNodeSize`
|
||||
- [x] 12.2: Unit tests for graph converter flowchart type mapping — node types correctly resolved for `diagramType === "flowchart"`, edge types resolved, flat layout (no containers)
|
||||
- [x] 12.3: Update "all 6 diagram types" test (line 150 of graph-converter.test.ts): change assertion from `"default"` to `"flowProcess"` for `flow:process` node
|
||||
- [x] 12.4: Unit tests for ELK layout flowchart node sizing — `buildElkGraph` produces correct dimensions for flowchart nodes
|
||||
- [x] 12.5: All tests pass — no regressions
|
||||
|
||||
## Dev Notes
|
||||
|
||||
### Overview — What This Story Builds
|
||||
|
||||
This story adds the Flowchart diagram type renderer: the last of the 6 core diagram types. Flowcharts use standard ISO 5807-inspired shapes (process, decision, terminal, I/O, subprocess) with the standard ELK layered algorithm for top-down flow layout.
|
||||
|
||||
**This story builds:**
|
||||
- 5 custom flowchart node components (FlowProcessNode, FlowDecisionNode, FlowTerminalNode, FlowIoNode, FlowSubprocessNode)
|
||||
- 1 custom flowchart edge component (FlowEdge) with label support for decision outcomes
|
||||
- Flowchart constants and type registry (5 node subtypes + 1 edge type)
|
||||
- Graph converter flowchart type resolution
|
||||
- ELK layout flowchart node sizing
|
||||
- Flowchart CSS styles with rose `--diagram-flowchart` accent
|
||||
- Flowchart arrow marker in MarkerDefs
|
||||
|
||||
**This story does NOT implement:**
|
||||
- Other diagram types (all 6 complete after this)
|
||||
- Smart Inspector for flowchart field editing (future epic)
|
||||
- Manual node repositioning (Story 2.9)
|
||||
- Liveblocks/CRDT integration (Epic 4)
|
||||
- AI-triggered mutations (Epic 3)
|
||||
- Nested diagrams (Epic 7)
|
||||
|
||||
### Architecture Compliance
|
||||
|
||||
**MANDATORY patterns from Architecture Decision Document:**
|
||||
|
||||
1. **Unified Graph Data Model (Decision 1):** Flowchart nodes use type-prefixed `type` field (`flow:process`, `flow:decision`, `flow:terminal`, `flow:io`, `flow:subprocess`). Existing DiagramNode fields are semantically reused: `label` = step description/question text, `tag` = category (e.g., "start"/"end" for terminals), `icon` = optional step emoji, `color` = custom accent override. Edge types use a single value: `sequence` (or default). NO new fields on DiagramNode or DiagramEdge needed.
|
||||
|
||||
2. **Component Structure:** Feature code in `~/modules/diagram/types/flowchart/` — follows the BPMN (`types/bpmn/`), E-R (`types/er/`), Org Chart (`types/orgchart/`), Architecture (`types/architecture/`), Sequence (`types/sequence/`) pattern.
|
||||
|
||||
3. **@xyflow/react Custom Nodes:** All custom node components use `NodeProps` typing. The `nodeTypes` and `edgeTypes` objects MUST be defined OUTSIDE the component (performance critical — established in Stories 2.1-2.7).
|
||||
|
||||
4. **Standard ELK Layout:** Flowchart uses the standard ELK layered algorithm — no custom layout needed (unlike sequence diagrams). `buildElkGraph()` needs flowchart node sizing via `getFlowNodeSize()`. Default direction: `DOWN` for top-down flow.
|
||||
|
||||
5. **Lean JSON Data Model:** No x/y positions stored for flowchart nodes. All positioning computed by ELK at render time.
|
||||
|
||||
6. **Type Prefixing Convention (Enforcement Rule #5):** `flow:` prefix on DiagramNode.type. Edge type is bare lowercase: `sequence` (or undefined for default).
|
||||
|
||||
### Flowchart Diagram Data Model — Field Mapping
|
||||
|
||||
The existing DiagramNode and DiagramEdge fields satisfy flowchart needs:
|
||||
|
||||
```typescript
|
||||
// flow:process node
|
||||
{
|
||||
id: "step1",
|
||||
type: "flow:process",
|
||||
label: "Process Payment", // Step description
|
||||
icon: "💳", // Optional step icon
|
||||
color: "#e11d48", // Custom accent (optional)
|
||||
}
|
||||
|
||||
// flow:decision node
|
||||
{
|
||||
id: "check1",
|
||||
type: "flow:decision",
|
||||
label: "Payment Valid?", // Question/condition text
|
||||
icon: "❓", // Optional icon
|
||||
}
|
||||
|
||||
// flow:terminal node (start)
|
||||
{
|
||||
id: "start",
|
||||
type: "flow:terminal",
|
||||
label: "Start",
|
||||
tag: "start", // Distinguishes start vs end
|
||||
}
|
||||
|
||||
// flow:terminal node (end)
|
||||
{
|
||||
id: "end",
|
||||
type: "flow:terminal",
|
||||
label: "End",
|
||||
tag: "end", // Distinguishes start vs end
|
||||
}
|
||||
|
||||
// flow:io node
|
||||
{
|
||||
id: "input1",
|
||||
type: "flow:io",
|
||||
label: "Read User Input",
|
||||
}
|
||||
|
||||
// flow:subprocess node
|
||||
{
|
||||
id: "sub1",
|
||||
type: "flow:subprocess",
|
||||
label: "Validate Credentials",
|
||||
}
|
||||
```
|
||||
|
||||
**Edge model:**
|
||||
```typescript
|
||||
// Standard flow edge
|
||||
{
|
||||
id: "e1",
|
||||
from: "start",
|
||||
to: "step1",
|
||||
// No label needed for sequential flow
|
||||
}
|
||||
|
||||
// Decision outcome edge
|
||||
{
|
||||
id: "e2",
|
||||
from: "check1",
|
||||
to: "step2",
|
||||
label: "Yes", // Decision outcome label
|
||||
}
|
||||
|
||||
// Decision outcome edge (alternative path)
|
||||
{
|
||||
id: "e3",
|
||||
from: "check1",
|
||||
to: "error1",
|
||||
label: "No", // Alternative outcome
|
||||
}
|
||||
```
|
||||
|
||||
### Flowchart Node Types — Size Constants
|
||||
|
||||
```typescript
|
||||
export const FLOW_SIZES = {
|
||||
process: { w: 160, h: 60 }, // Standard rectangle
|
||||
decision: { w: 140, h: 80 }, // Diamond (wider for rotation)
|
||||
terminal: { w: 140, h: 50 }, // Rounded rectangle / stadium
|
||||
io: { w: 160, h: 60 }, // Parallelogram
|
||||
subprocess: { w: 160, h: 60 }, // Double-bordered rectangle
|
||||
} as const;
|
||||
```
|
||||
|
||||
### Flowchart Node Type → @xyflow/react Type Mapping
|
||||
|
||||
| DiagramNode.type | @xyflow Node type | Component | Visual |
|
||||
|---|---|---|---|
|
||||
| `flow:process` | `flowProcess` | `FlowProcessNode` | Rectangle with label |
|
||||
| `flow:decision` | `flowDecision` | `FlowDecisionNode` | Diamond with condition text |
|
||||
| `flow:terminal` | `flowTerminal` | `FlowTerminalNode` | Rounded rectangle / stadium |
|
||||
| `flow:io` | `flowIo` | `FlowIoNode` | Parallelogram |
|
||||
| `flow:subprocess` | `flowSubprocess` | `FlowSubprocessNode` | Double-bordered rectangle |
|
||||
|
||||
**Type resolution:**
|
||||
```typescript
|
||||
export function resolveFlowchartNodeType(type: string): string {
|
||||
const bare = type.startsWith("flow:") ? type.slice(5) : type;
|
||||
switch (bare) {
|
||||
case "process":
|
||||
return "flowProcess";
|
||||
case "decision":
|
||||
return "flowDecision";
|
||||
case "terminal":
|
||||
return "flowTerminal";
|
||||
case "io":
|
||||
return "flowIo";
|
||||
case "subprocess":
|
||||
return "flowSubprocess";
|
||||
default:
|
||||
return "flowProcess"; // Default to process
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveFlowchartEdgeType(_type: string | undefined): string {
|
||||
return "flowEdge"; // Single edge type for flowcharts
|
||||
}
|
||||
|
||||
export function getFlowNodeSize(
|
||||
flowType: string | undefined,
|
||||
): { w: number; h: number } | null {
|
||||
switch (flowType) {
|
||||
case "flowProcess":
|
||||
return FLOW_SIZES.process;
|
||||
case "flowDecision":
|
||||
return FLOW_SIZES.decision;
|
||||
case "flowTerminal":
|
||||
return FLOW_SIZES.terminal;
|
||||
case "flowIo":
|
||||
return FLOW_SIZES.io;
|
||||
case "flowSubprocess":
|
||||
return FLOW_SIZES.subprocess;
|
||||
default:
|
||||
return null; // Not a flowchart node
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Flowchart Edge Type
|
||||
|
||||
| DiagramEdge.type | @xyflow Edge type | Visual |
|
||||
|---|---|---|
|
||||
| `sequence` (or default/undefined) | `flowEdge` | Solid orthogonal path with filled arrowhead, optional label at midpoint |
|
||||
|
||||
Only one edge type needed. Decision outcomes use the `label` field on the edge (e.g., "Yes", "No", "True", "False").
|
||||
|
||||
### Standard ELK Layout — Flowchart Uses the Default Path
|
||||
|
||||
**Flowchart diagrams USE the standard ELK layered algorithm.** No custom layout function needed. The `computeLayout()` function in `elk-layout.ts` routes through the default path — the only change needed is adding `getFlowNodeSize()` to the `buildElkGraph()` sizing chain.
|
||||
|
||||
**ELK settings for flowcharts:**
|
||||
- Algorithm: `layered` (default)
|
||||
- Direction: `DOWN` (top-down flow — best for flowcharts)
|
||||
- Edge routing: `ORTHOGONAL` (right-angle connectors — standard for flowcharts)
|
||||
- Crossing minimization: `LAYER_SWEEP` (default)
|
||||
- Node placement: `BRANDES_KOEPF` (default)
|
||||
|
||||
**Integration in `buildElkGraph()` — sizing chain update:**
|
||||
```typescript
|
||||
// After existing seqSize check:
|
||||
const flowSize = getFlowNodeSize(node.type);
|
||||
const height =
|
||||
isErEntity && data.columns
|
||||
? getErEntityHeight(data.columns)
|
||||
: isOcPerson
|
||||
? OC_SIZES.person.h
|
||||
: archSize
|
||||
? archSize.h
|
||||
: seqSize
|
||||
? seqSize.h
|
||||
: flowSize
|
||||
? flowSize.h
|
||||
: (node.measured?.height ?? DEFAULT_NODE_HEIGHT);
|
||||
const width = archSize
|
||||
? (data.w ?? archSize.w)
|
||||
: seqSize
|
||||
? (data.w ?? seqSize.w)
|
||||
: flowSize
|
||||
? (data.w ?? flowSize.w)
|
||||
: isOcPerson
|
||||
? (data.w ?? OC_SIZES.person.w)
|
||||
: (data.w ?? node.measured?.width ?? DEFAULT_NODE_WIDTH);
|
||||
```
|
||||
|
||||
### Node Component Patterns
|
||||
|
||||
All 5 node components follow the established pattern:
|
||||
|
||||
```typescript
|
||||
import { Handle, Position } from "@xyflow/react";
|
||||
import type { NodeProps } from "@xyflow/react";
|
||||
import type { DiagramNode } from "../graph";
|
||||
import { HIDDEN_HANDLE } from "../architecture/constants";
|
||||
|
||||
export function FlowProcessNode({ data }: NodeProps) {
|
||||
const d = data as unknown as DiagramNode & { label: string };
|
||||
const icon = d.icon || "⚙️";
|
||||
|
||||
return (
|
||||
<div className="flow-process">
|
||||
<span className="flow-node-icon">{icon}</span>
|
||||
<span className="flow-node-label">{d.label}</span>
|
||||
<Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
|
||||
<Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Decision node — Diamond shape via CSS:**
|
||||
```typescript
|
||||
export function FlowDecisionNode({ data }: NodeProps) {
|
||||
const d = data as unknown as DiagramNode & { label: string };
|
||||
|
||||
return (
|
||||
<div className="flow-decision">
|
||||
<div className="flow-decision-diamond">
|
||||
<span className="flow-decision-label">{d.label}</span>
|
||||
</div>
|
||||
<Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
|
||||
<Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Terminal node — Stadium/pill shape:**
|
||||
```typescript
|
||||
export function FlowTerminalNode({ data }: NodeProps) {
|
||||
const d = data as unknown as DiagramNode & { label: string };
|
||||
const isStart = d.tag === "start";
|
||||
const isEnd = d.tag === "end";
|
||||
|
||||
return (
|
||||
<div className={`flow-terminal ${isStart ? "flow-terminal-start" : ""} ${isEnd ? "flow-terminal-end" : ""}`}>
|
||||
<span className="flow-node-label">{d.label}</span>
|
||||
<Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
|
||||
<Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**I/O node — Parallelogram via CSS skew:**
|
||||
```typescript
|
||||
export function FlowIoNode({ data }: NodeProps) {
|
||||
const d = data as unknown as DiagramNode & { label: string };
|
||||
|
||||
return (
|
||||
<div className="flow-io">
|
||||
<div className="flow-io-skew">
|
||||
<span className="flow-node-label">{d.label}</span>
|
||||
</div>
|
||||
<Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
|
||||
<Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Subprocess node — Double-bordered rectangle:**
|
||||
```typescript
|
||||
export function FlowSubprocessNode({ data }: NodeProps) {
|
||||
const d = data as unknown as DiagramNode & { label: string };
|
||||
|
||||
return (
|
||||
<div className="flow-subprocess">
|
||||
<div className="flow-subprocess-inner">
|
||||
<span className="flow-node-label">{d.label}</span>
|
||||
</div>
|
||||
<Handle type="target" position={Position.Top} style={HIDDEN_HANDLE} />
|
||||
<Handle type="target" position={Position.Left} id="left" style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Bottom} style={HIDDEN_HANDLE} />
|
||||
<Handle type="source" position={Position.Right} id="right" style={HIDDEN_HANDLE} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
**Reuses `HIDDEN_HANDLE`** from `architecture/constants.ts` (established in Story 2.6).
|
||||
|
||||
### Edge Component — Standard Orthogonal with Label
|
||||
|
||||
```typescript
|
||||
import { BaseEdge, getSmoothStepPath, EdgeLabelRenderer } from "@xyflow/react";
|
||||
import type { EdgeProps } from "@xyflow/react";
|
||||
|
||||
export function FlowEdge(props: EdgeProps) {
|
||||
const [edgePath, labelX, labelY] = getSmoothStepPath({
|
||||
sourceX: props.sourceX,
|
||||
sourceY: props.sourceY,
|
||||
targetX: props.targetX,
|
||||
targetY: props.targetY,
|
||||
sourcePosition: props.sourcePosition,
|
||||
targetPosition: props.targetPosition,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseEdge
|
||||
id={props.id}
|
||||
path={edgePath}
|
||||
style={{ stroke: "var(--diagram-flowchart)", strokeWidth: 1.5 }}
|
||||
markerEnd="url(#flow-arrow)"
|
||||
/>
|
||||
{props.label && (
|
||||
<EdgeLabelRenderer>
|
||||
<div
|
||||
className="flow-edge-label"
|
||||
style={{
|
||||
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,
|
||||
position: "absolute",
|
||||
}}
|
||||
>
|
||||
{props.label}
|
||||
</div>
|
||||
</EdgeLabelRenderer>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Uses `getSmoothStepPath` for orthogonal routing (right-angle connectors). `EdgeLabelRenderer` for HTML label at midpoint — critical for decision outcome labels (Yes/No).
|
||||
|
||||
### Graph Converter Updates
|
||||
|
||||
Replace the `// Future: flowchart` comment with real dispatch:
|
||||
|
||||
```typescript
|
||||
// In resolveFlowNodeType():
|
||||
if (diagramType === "flowchart" || nodeType.startsWith("flow:")) {
|
||||
return resolveFlowchartNodeType(nodeType);
|
||||
}
|
||||
// Remove: // Future: flowchart
|
||||
return "default"; // Truly unknown types
|
||||
|
||||
// In resolveFlowEdgeType():
|
||||
if (diagramType === "flowchart") {
|
||||
return resolveFlowchartEdgeType(edgeType);
|
||||
}
|
||||
return "default";
|
||||
```
|
||||
|
||||
**Import from flowchart/constants.ts** — same pattern as BPMN, E-R, Org Chart, Architecture, Sequence.
|
||||
|
||||
**graphToFlow for flowchart:** Uses the standard default path (flat node mapping). No container nodes. No compound layout.
|
||||
|
||||
**No CONTAINER_TYPES update needed:** Flowchart has no container nodes (unlike BPMN pools/lanes or sequence fragments).
|
||||
|
||||
### DiagramCanvas Updates
|
||||
|
||||
```typescript
|
||||
import { FlowProcessNode } from "../../types/flowchart/FlowProcessNode";
|
||||
import { FlowDecisionNode } from "../../types/flowchart/FlowDecisionNode";
|
||||
import { FlowTerminalNode } from "../../types/flowchart/FlowTerminalNode";
|
||||
import { FlowIoNode } from "../../types/flowchart/FlowIoNode";
|
||||
import { FlowSubprocessNode } from "../../types/flowchart/FlowSubprocessNode";
|
||||
import { FlowEdge } from "../../types/flowchart/FlowEdge";
|
||||
|
||||
const nodeTypes = {
|
||||
// Existing BPMN + E-R + Org Chart + Architecture + Sequence types...
|
||||
flowProcess: FlowProcessNode,
|
||||
flowDecision: FlowDecisionNode,
|
||||
flowTerminal: FlowTerminalNode,
|
||||
flowIo: FlowIoNode,
|
||||
flowSubprocess: FlowSubprocessNode,
|
||||
};
|
||||
|
||||
const edgeTypes = {
|
||||
// Existing types...
|
||||
flowEdge: FlowEdge,
|
||||
};
|
||||
|
||||
// CONTAINER_TYPES — NO CHANGE needed (no flowchart containers)
|
||||
```
|
||||
|
||||
**Arrow marker needed in MarkerDefs:**
|
||||
```tsx
|
||||
{/* Flowchart markers */}
|
||||
<marker
|
||||
id="flow-arrow"
|
||||
viewBox="0 0 10 10"
|
||||
refX={10}
|
||||
refY={5}
|
||||
markerWidth={8}
|
||||
markerHeight={8}
|
||||
orient="auto-start-reverse"
|
||||
>
|
||||
<path
|
||||
d="M 0 0 L 10 5 L 0 10 Z"
|
||||
fill="var(--diagram-flowchart, #e11d48)"
|
||||
/>
|
||||
</marker>
|
||||
```
|
||||
|
||||
### CSS Styles for Flowchart
|
||||
|
||||
Add to `globals.css`. The flowchart theme uses `--diagram-flowchart` (rose accent: `oklch(0.645 0.246 16)`, already defined at line 29).
|
||||
|
||||
```css
|
||||
/* ── Flowchart Diagram Styles ─────────────────────────────────── */
|
||||
|
||||
.flow-process {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: var(--node-bg);
|
||||
border: 1.5px solid var(--diagram-flowchart);
|
||||
border-radius: 6px;
|
||||
padding: 10px 16px;
|
||||
min-width: 120px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.flow-process:hover {
|
||||
background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
|
||||
}
|
||||
|
||||
.flow-decision {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 140px;
|
||||
height: 80px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.flow-decision-diamond {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
transform: rotate(45deg);
|
||||
background: var(--node-bg);
|
||||
border: 1.5px solid var(--diagram-flowchart);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.flow-decision-diamond:hover {
|
||||
background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
|
||||
}
|
||||
|
||||
.flow-decision-label {
|
||||
transform: rotate(-45deg);
|
||||
font-weight: 600;
|
||||
font-size: 11px;
|
||||
color: var(--foreground);
|
||||
text-align: center;
|
||||
max-width: 80px;
|
||||
word-wrap: break-word;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.flow-terminal {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--node-bg);
|
||||
border: 1.5px solid var(--diagram-flowchart);
|
||||
border-radius: 999px;
|
||||
padding: 10px 24px;
|
||||
min-width: 100px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.flow-terminal:hover {
|
||||
background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
|
||||
}
|
||||
.flow-terminal-start {
|
||||
border-color: var(--diagram-flowchart);
|
||||
border-width: 2px;
|
||||
}
|
||||
.flow-terminal-end {
|
||||
border-color: var(--diagram-flowchart);
|
||||
border-width: 2.5px;
|
||||
}
|
||||
|
||||
.flow-io {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.flow-io-skew {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
background: var(--node-bg);
|
||||
border: 1.5px solid var(--diagram-flowchart);
|
||||
padding: 10px 20px;
|
||||
transform: skewX(-10deg);
|
||||
min-width: 120px;
|
||||
}
|
||||
.flow-io-skew:hover {
|
||||
background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
|
||||
}
|
||||
.flow-io-skew .flow-node-label {
|
||||
transform: skewX(10deg);
|
||||
}
|
||||
|
||||
.flow-subprocess {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--node-bg);
|
||||
border: 2px solid var(--diagram-flowchart);
|
||||
border-radius: 6px;
|
||||
padding: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.flow-subprocess:hover {
|
||||
background: color-mix(in oklch, var(--diagram-flowchart) 8%, var(--node-bg));
|
||||
}
|
||||
|
||||
.flow-subprocess-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border: 1px solid var(--diagram-flowchart);
|
||||
border-radius: 4px;
|
||||
padding: 8px 14px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.flow-node-icon {
|
||||
font-size: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.flow-node-label {
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
color: var(--foreground);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.flow-edge-label {
|
||||
font-size: 11px;
|
||||
color: var(--diagram-flowchart);
|
||||
background: var(--node-bg);
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid color-mix(in oklch, var(--diagram-flowchart) 30%, transparent);
|
||||
font-weight: 600;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
```
|
||||
|
||||
### Existing Code to Reuse / Modify
|
||||
|
||||
| File | Action | What |
|
||||
|------|--------|------|
|
||||
| `apps/web/src/modules/diagram/components/editor/DiagramCanvas.tsx` | **MODIFY** | Register flowchart node types, edge type, arrow marker |
|
||||
| `apps/web/src/modules/diagram/lib/graph-converter.ts` | **MODIFY** | Add flowchart type resolution for nodes and edges (replace `// Future: flowchart` comment) |
|
||||
| `apps/web/src/modules/diagram/lib/elk-layout.ts` | **MODIFY** | Add `getFlowNodeSize()` to buildElkGraph sizing chain |
|
||||
| `apps/web/src/assets/styles/globals.css` | **MODIFY** | Add flowchart CSS styles |
|
||||
| `apps/web/src/modules/diagram/types/graph.ts` | **READ** | DiagramNode (tag, icon, color) — already defined, no changes |
|
||||
| `apps/web/src/modules/diagram/lib/bfs-path.ts` | **REUSE** | Path highlighting works for flowchart — diagram-type-agnostic |
|
||||
| `apps/web/src/modules/diagram/stores/useGraphStore.ts` | **REUSE** | highlightedNodeId — no changes needed |
|
||||
| `apps/web/src/modules/diagram/hooks/useAutoLayout.ts` | **REUSE** | Auto-layout triggers computeLayout → standard ELK path for flowchart |
|
||||
| `apps/web/src/modules/diagram/types/architecture/constants.ts` | **REUSE** | `HIDDEN_HANDLE` constant for handle styles |
|
||||
|
||||
### Library & Framework Requirements
|
||||
|
||||
**No new packages required.** Everything built with existing dependencies:
|
||||
- `@xyflow/react` 12.10.1 — custom nodes, edges, handles, EdgeLabelRenderer, BaseEdge, getSmoothStepPath
|
||||
- `elkjs` 0.11.0 — standard layered algorithm for flowchart layout
|
||||
- `zustand` 5.0.8 — highlight state (reuse existing)
|
||||
|
||||
### File Structure for This Story
|
||||
|
||||
New files:
|
||||
```
|
||||
apps/web/src/modules/diagram/
|
||||
├── types/flowchart/
|
||||
│ ├── constants.ts # FLOW_SIZES, type resolution functions, getFlowNodeSize
|
||||
│ ├── constants.test.ts # Tests for flowchart constants
|
||||
│ ├── FlowProcessNode.tsx # Rectangle - standard process step
|
||||
│ ├── FlowDecisionNode.tsx # Diamond - decision/branching
|
||||
│ ├── FlowTerminalNode.tsx # Stadium/pill - start and end nodes
|
||||
│ ├── FlowIoNode.tsx # Parallelogram - input/output
|
||||
│ ├── FlowSubprocessNode.tsx # Double-bordered rectangle - subprocess
|
||||
│ └── FlowEdge.tsx # Orthogonal edge with label support
|
||||
```
|
||||
|
||||
Modified files:
|
||||
```
|
||||
apps/web/src/modules/diagram/components/editor/DiagramCanvas.tsx # Register flowchart types + marker
|
||||
apps/web/src/modules/diagram/lib/graph-converter.ts # Flowchart type mapping (replace // Future: flowchart)
|
||||
apps/web/src/modules/diagram/lib/graph-converter.test.ts # Add flowchart converter tests + fix line 150 assertion
|
||||
apps/web/src/modules/diagram/lib/elk-layout.ts # Flowchart node sizing in buildElkGraph
|
||||
apps/web/src/modules/diagram/lib/elk-layout.test.ts # Add flowchart sizing tests
|
||||
apps/web/src/assets/styles/globals.css # Flowchart CSS styles
|
||||
```
|
||||
|
||||
### Project Structure Notes
|
||||
|
||||
- Flowchart node components go in `~/modules/diagram/types/flowchart/` — follows BPMN/E-R/Org Chart/Architecture/Sequence pattern
|
||||
- Uses standard ELK layout — no separate layout module needed (unlike sequence)
|
||||
- Tests co-located next to source files
|
||||
- No barrel files — import from specific subpaths
|
||||
|
||||
### Anti-Patterns to Avoid
|
||||
|
||||
- **NEVER put `nodeTypes` or `edgeTypes` inside the component** — causes re-renders (established pattern)
|
||||
- **NEVER hardcode positions for flowchart nodes in GraphData** — all positioning from ELK layout
|
||||
- **NEVER import from `reactflow`** — use `@xyflow/react` (v12+)
|
||||
- **NEVER use `require()`** — ESM-only project
|
||||
- **NEVER co-locate feature code in route directories** — use `~/modules/diagram/`
|
||||
- **NEVER store layout-computed positions in persisted graph data** — positions are ephemeral
|
||||
- **NEVER add new fields to DiagramNode or DiagramEdge** — use existing fields (`tag`, `icon`, `color`) with flowchart semantics
|
||||
- **NEVER create inline style objects in render** — use CSS classes; conditionally apply inline styles only when data-driven
|
||||
- **NEVER use `??` for empty string fallbacks** — use `||` so falsy `""` falls back correctly (lesson from Story 2.5 code review)
|
||||
- **NEVER create barrel `index.ts` files** — per project rules, no barrel files in feature modules
|
||||
- **DO NOT implement Smart Inspector for flowchart** — future story
|
||||
- **DO NOT implement other diagram type renderers** — all 6 are now complete
|
||||
- **DO NOT break existing tests** — 476 tests must continue passing (29 test files across monorepo)
|
||||
- **DO NOT create a custom layout for flowchart** — ELK layered algorithm handles flowcharts perfectly
|
||||
- **DO NOT add arrow markers inline** — use SVG `<marker>` in `MarkerDefs` with `markerEnd="url(#flow-arrow)"`
|
||||
- **DO NOT create multiple edge types** — flowcharts need only one edge type with optional labels for decision outcomes
|
||||
|
||||
### Previous Story Intelligence (Story 2.7 — Sequence)
|
||||
|
||||
**Key learnings to carry forward:**
|
||||
- Constants file pattern: `SEQ_SIZES` → `FLOW_SIZES` equivalent. Type resolution functions: `resolve*NodeType()`, `resolve*EdgeType()`, `get*NodeSize()`
|
||||
- Node component pattern: Cast `data` as `DiagramNode & { label: string }`, use `Handle` with `style={HIDDEN_HANDLE}` (constant from architecture/constants.ts — reuse it)
|
||||
- Edge component: For flowchart, use `BaseEdge` + `getSmoothStepPath` (unlike sequence which uses custom SVG paths). This is simpler — matches the E-R/Architecture/OrgChart pattern
|
||||
- Graph converter: `resolveFlowNodeType` switch on `diagramType`, import type resolver from `types/[type]/constants`
|
||||
- DiagramCanvas: import components, add to `nodeTypes`/`edgeTypes` objects OUTSIDE component
|
||||
- CSS: Use `--diagram-[type]` CSS variable for accent colors. Use `color-mix()` for hover/bg tints
|
||||
- `graphNodeToFlowNode` already spreads all DiagramNode fields into `data` — custom nodes access `data.tag`, `data.icon`, `data.color` directly
|
||||
- `flowNodeToGraphNode` already preserves `tag`, `icon`, `color` fields — roundtrip works
|
||||
- Code review lessons from previous stories: Remove dead helper functions, use `||` not `??` for empty string color guards, reuse `HIDDEN_HANDLE` constant
|
||||
- 476 tests currently pass across monorepo — don't break them
|
||||
- `computeLayout` returns `LayoutResult{nodes, edges?}` — flowchart uses standard path so `edges` is not returned (only ELK node positions updated)
|
||||
|
||||
### Previous Story Intelligence (Story 2.3 — BPMN)
|
||||
|
||||
**Key learnings relevant to flowchart:**
|
||||
- BPMN's `MarkerDefs` pattern: `#bpmn-arrow-filled` — reuse same SVG marker structure for `#flow-arrow`
|
||||
- Standard edge with `BaseEdge` + `getSmoothStepPath` is the simplest edge pattern — use it for flowchart
|
||||
- BPMN's compound detection in `computeLayout()` shows the detection routing pattern — flowchart does NOT need this (uses standard path)
|
||||
|
||||
### Git Intelligence
|
||||
|
||||
Recent commits:
|
||||
- `1ff8ff8 feat: implement Stories 2.4-2.7 — E-R, Org Chart, Architecture, Sequence diagram type renderers`
|
||||
- `0a7838a feat: implement Story 2.3 — BPMN diagram type renderer`
|
||||
- `7dd5af1 feat: implement Story 2.2 — ELK.js auto-layout engine in Web Worker`
|
||||
- `5033109 feat: implement Story 2.1 — canvas workspace with @xyflow/react and unified graph model`
|
||||
|
||||
Established patterns:
|
||||
- Commit message: `feat: implement Story X.Y — description`
|
||||
- Feature code in `apps/web/src/modules/diagram/`
|
||||
- Co-located tests next to source files
|
||||
- `diagramTypeConfig` in DiagramCard.tsx already has flowchart config: `{ label: "Flowchart", icon: Icons.GitBranch, color: "text-rose-500" }`
|
||||
- CSS variable `--diagram-flowchart: oklch(0.645 0.246 16)` already defined in globals.css (line 29)
|
||||
- Graph-converter.ts line 50 has `// Future: flowchart` — the exact hook point to replace
|
||||
- Graph-converter.test.ts line 150 expects `"default"` for `flow:process` — must update to `"flowProcess"` after adding resolver
|
||||
|
||||
### Latest Tech Information
|
||||
|
||||
**@xyflow/react 12.10.1 — BaseEdge + getSmoothStepPath:**
|
||||
- `getSmoothStepPath` produces orthogonal (right-angle) edge paths — ideal for flowcharts
|
||||
- Returns `[path, labelX, labelY]` tuple — `labelX`/`labelY` are the midpoint coordinates for label placement
|
||||
- `BaseEdge` wraps the path in a proper SVG `<path>` with all @xyflow/react edge features (selection, animation, etc.)
|
||||
- `EdgeLabelRenderer` renders HTML labels in a separate div layer above the SVG — use for decision labels
|
||||
- `markerEnd` on `BaseEdge` references SVG markers from `<defs>` — same as all other diagram types
|
||||
|
||||
**CSS Diamond Shape for Decision Nodes:**
|
||||
- Standard technique: `transform: rotate(45deg)` on container, `transform: rotate(-45deg)` on content
|
||||
- Container dimensions should be square (the diamond "width" = side * √2)
|
||||
- Handle positions work correctly with transformed containers in @xyflow/react — handles are positioned on the outer bounding box
|
||||
- Alternative: Use CSS `clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%)` — simpler but may have handle positioning issues
|
||||
|
||||
**CSS Parallelogram for I/O Nodes:**
|
||||
- `transform: skewX(-10deg)` on container, `transform: skewX(10deg)` on content (counter-skew)
|
||||
- Keeps text readable while giving the parallelogram visual
|
||||
- Handles work correctly since @xyflow/react uses bounding box positioning
|
||||
|
||||
### References
|
||||
|
||||
- [Source: _bmad-output/planning-artifacts/epics.md#Story 2.8] — Full AC: process, decision, terminal, I/O, subprocess shapes; decision labels; ELK top-down layout
|
||||
- [Source: _bmad-output/planning-artifacts/epics.md#Technical Notes] — Port Flexicar FlowDiagram nodes; custom nodes at types/flowchart/; rose accent; standard ELK layered; parallel development
|
||||
- [Source: _bmad-output/planning-artifacts/architecture.md#Decision 1] — Unified Graph Data Model: `flow:` prefix, shared base fields
|
||||
- [Source: _bmad-output/planning-artifacts/architecture.md#Enforcement Guidelines] — 7 mandatory rules, type prefixing: `flow:`
|
||||
- [Source: _bmad-output/planning-artifacts/ux-design-specification.md#Diagram Type Color Accents] — Flowchart: rose oklch(0.645 0.246 16)
|
||||
- [Source: _bmad-output/planning-artifacts/ux-design-specification.md#DiagramEdgeRenderer] — Flowchart edges: standard directed flow
|
||||
- [Source: _bmad-output/implementation-artifacts/2-7-sequence-diagram-type-renderer.md] — Sequence patterns: constants, nodes, edges, converter, canvas registration, HIDDEN_HANDLE, code review learnings
|
||||
- [Source: apps/web/src/modules/diagram/types/graph.ts] — DiagramNode (tag, icon, color), DiagramType includes "flowchart" (line 7)
|
||||
- [Source: apps/web/src/modules/diagram/lib/graph-converter.ts] — Line 50: `// Future: flowchart` — exact hook point to replace
|
||||
- [Source: apps/web/src/modules/diagram/lib/graph-converter.test.ts] — Line 150: `flow:process` currently resolves to "default" — update to "flowProcess"
|
||||
- [Source: apps/web/src/modules/diagram/lib/elk-layout.ts] — buildElkGraph() sizing chain needs flowSize addition after seqSize
|
||||
- [Source: apps/web/src/modules/diagram/components/editor/DiagramCanvas.tsx] — Canvas to register flowchart types + marker
|
||||
- [Source: apps/web/src/modules/diagram/components/DiagramCard.tsx] — Flowchart config with Icons.GitBranch and text-rose-500
|
||||
- [Source: apps/web/src/assets/styles/globals.css] — Line 29: `--diagram-flowchart: oklch(0.645 0.246 16)` already defined
|
||||
|
||||
## Dev Agent Record
|
||||
|
||||
### Agent Model Used
|
||||
|
||||
Claude Opus 4.6 (1M context)
|
||||
|
||||
### Debug Log References
|
||||
|
||||
No issues encountered. One pre-existing test (`graphNodeToFlowNode` basic node) expected `"default"` type for `flow:process` — updated to `"flowProcess"` since the `flow:` prefix now correctly resolves via the new flowchart resolver.
|
||||
|
||||
### Completion Notes List
|
||||
|
||||
- Created flowchart constants with FLOW_SIZES (5 subtypes), resolveFlowchartNodeType, resolveFlowchartEdgeType, getFlowNodeSize using FLOW_TYPE_MAP for O(1) lookup
|
||||
- Implemented FlowProcessNode: rectangle with icon + label, rose accent border, 4 hidden handles
|
||||
- Implemented FlowDecisionNode: diamond shape via CSS rotate(45deg) with counter-rotated label
|
||||
- Implemented FlowTerminalNode: stadium/pill shape (border-radius: 999px), data.tag-based start/end distinction with varying border widths
|
||||
- Implemented FlowIoNode: parallelogram shape via CSS skewX(-10deg) with counter-skewed label
|
||||
- Implemented FlowSubprocessNode: double-bordered rectangle (outer 2px + inner 1px borders)
|
||||
- Implemented FlowEdge: BaseEdge + getSmoothStepPath for orthogonal routing, EdgeLabelRenderer for decision outcome labels (Yes/No), markerEnd flow-arrow
|
||||
- Updated graph-converter.ts: replaced `// Future: flowchart` with real dispatch for both node and edge resolution, added flowchart edge type handling
|
||||
- Registered all flowchart node/edge types in DiagramCanvas nodeTypes/edgeTypes (OUTSIDE component), added #flow-arrow SVG marker to MarkerDefs
|
||||
- Added getFlowNodeSize to elk-layout.ts buildElkGraph sizing chain after seqSize
|
||||
- Added flowchart CSS styles with --diagram-flowchart rose accent, color-mix hover states, all 5 node shapes + edge label styling
|
||||
- Updated existing test assertions: flow:process now resolves to flowProcess (previously "default")
|
||||
- Added 15 flowchart constants tests, 6 graph converter flowchart tests, 4 ELK layout flowchart sizing tests
|
||||
- All 497 tests pass across 30 test files (0 regressions, +21 new tests)
|
||||
|
||||
### File List
|
||||
|
||||
**New files:**
|
||||
- apps/web/src/modules/diagram/types/flowchart/constants.ts
|
||||
- apps/web/src/modules/diagram/types/flowchart/constants.test.ts
|
||||
- apps/web/src/modules/diagram/types/flowchart/FlowProcessNode.tsx
|
||||
- apps/web/src/modules/diagram/types/flowchart/FlowDecisionNode.tsx
|
||||
- apps/web/src/modules/diagram/types/flowchart/FlowTerminalNode.tsx
|
||||
- apps/web/src/modules/diagram/types/flowchart/FlowIoNode.tsx
|
||||
- apps/web/src/modules/diagram/types/flowchart/FlowSubprocessNode.tsx
|
||||
- apps/web/src/modules/diagram/types/flowchart/FlowEdge.tsx
|
||||
|
||||
**Modified files:**
|
||||
- apps/web/src/modules/diagram/lib/graph-converter.ts
|
||||
- apps/web/src/modules/diagram/lib/graph-converter.test.ts
|
||||
- apps/web/src/modules/diagram/lib/elk-layout.ts
|
||||
- apps/web/src/modules/diagram/lib/elk-layout.test.ts
|
||||
- apps/web/src/modules/diagram/components/editor/DiagramCanvas.tsx
|
||||
- apps/web/src/assets/styles/globals.css
|
||||
- _bmad-output/implementation-artifacts/sprint-status.yaml
|
||||
|
||||
### Change Log
|
||||
|
||||
- 2026-02-27: Implemented Story 2.8 — Flowchart Diagram Type Renderer. Added 5 node components (process, decision, terminal, I/O, subprocess), 1 edge component with label support, constants + type registry, graph converter integration, ELK layout sizing, CSS styles with rose accent, DiagramCanvas registration + arrow marker. 497 tests passing.
|
||||
- 2026-02-27: Code review (adversarial) — 7 issues found (4M, 3L), all fixed. M1: Decision diamond ELK height increased 80→130 to fit rotated 90px square. M2: Removed forced ⚙️ icon default from FlowProcessNode, now only renders when data.icon is truthy. M3: Added data.color border override support to all 5 node components. M4: Extracted getNodeDimensions() helper in elk-layout.ts to replace 6-level nested ternary. L1: Unified constants.ts to use lookup maps for both type resolution and sizing. L2: Removed dead gap:8px from .flow-io-skew CSS. L3: Added sprint-status.yaml to File List. 180 web tests passing, 0 regressions.
|
||||
@@ -57,7 +57,7 @@ development_status:
|
||||
2-5-org-chart-diagram-type-renderer: done
|
||||
2-6-architecture-diagram-type-renderer: done
|
||||
2-7-sequence-diagram-type-renderer: done
|
||||
2-8-flowchart-diagram-type-renderer: backlog
|
||||
2-8-flowchart-diagram-type-renderer: done
|
||||
2-9-node-selection-and-manual-repositioning: backlog
|
||||
epic-2-retrospective: optional
|
||||
|
||||
|
||||
Reference in New Issue
Block a user