This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts-intellij-plugin.git


The following commit(s) were added to refs/heads/main by this push:
     new e09fd34  Show Diagram: space multi-result edge labels in LTR (#128) 
(#131)
e09fd34 is described below

commit e09fd34155dc3e9c26b2c479d9f76fc0e25f7b52
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sun Jul 26 10:55:49 2026 +0200

    Show Diagram: space multi-result edge labels in LTR (#128) (#131)
    
    * docs: design for Show Diagram result edge label spacing (#128)
    
    Capture the approved spacing-only approach: Maven/Gradle-style
    minimal node/layer distance 20 on the custom LTR layouter.
    
    Co-authored-by: Cursor <[email protected]>
    
    * docs: plan Show Diagram result edge label spacing (#128)
    
    Co-authored-by: Cursor <[email protected]>
    
    * test(diagram): require Maven-style Show Diagram layouter distances (#128)
    
    Co-authored-by: Cursor <[email protected]>
    
    * fix(diagram): space Show Diagram result edge labels in LTR (#128)
    
    Co-authored-by: Cursor <[email protected]>
    
    * docs: note Show Diagram result edge label spacing (#128)
    
    Co-authored-by: Cursor <[email protected]>
    
    * docs: amend #128 spacing design above platform defaults
    
    Record why Maven/Gradle's unlabeled 20/20 compaction is unsuitable and 
choose a moderate 40/60 bump for labeled LTR graphs.
    
    Co-authored-by: Cursor <[email protected]>
    
    * fix(diagram): use 40/60 layouter distances (#128)
    
    Raise node and layer spacing above IU defaults so multi-result labels have 
meaningful vertical and horizontal room.
    
    Co-authored-by: Cursor <[email protected]>
    
    ---------
    
    Co-authored-by: Cursor <[email protected]>
---
 CHANGELOG.md                                       |   1 +
 ...07-26-show-diagram-result-edge-label-spacing.md | 307 +++++++++++++++++++++
 ...how-diagram-result-edge-label-spacing-design.md | 160 +++++++++++
 .../diagram/provider/StrutsDiagramExtras.java      |   5 +
 .../struts2/diagram/StrutsDiagramProviderTest.java |  14 +
 5 files changed, 487 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e12a852..9ade112 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -23,6 +23,7 @@
 ### Fixed
 
 - Fix Show Diagram stale result path labels after Refresh Data Model following 
copy-paste / path edits 
([#126](https://github.com/apache/struts-intellij-plugin/issues/126))
+- Fix Show Diagram cramped multi-result edge labels under left-to-right layout 
([#128](https://github.com/apache/struts-intellij-plugin/issues/128))
 
 ## [261.19027.1] - 2026-07-13
 
diff --git 
a/docs/superpowers/plans/2026-07-26-show-diagram-result-edge-label-spacing.md 
b/docs/superpowers/plans/2026-07-26-show-diagram-result-edge-label-spacing.md
new file mode 100644
index 0000000..b6d1aae
--- /dev/null
+++ 
b/docs/superpowers/plans/2026-07-26-show-diagram-result-edge-label-spacing.md
@@ -0,0 +1,307 @@
+# Show Diagram Result Edge Label Spacing Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use 
superpowers:subagent-driven-development (recommended) or 
superpowers:executing-plans to implement this plan task-by-task. Steps use 
checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Give the custom LTR Show Diagram layouter node/layer distances above 
the platform defaults so multi-result edge labels stay readable without 
overlapping each other or the action node.
+
+**Architecture:** Keep the existing Show Diagram provider/data model and LTR 
custom layouter. IU 2026.2 defaults to node distance `20.0` and layer distance 
`40.0`; in `StrutsDiagramExtras.getCustomLayouter`, after setting 
`LEFT_TO_RIGHT`, raise them to `setMinimalNodeDistance(40.0)` and 
`setMinimalLayerDistance(60.0)`. Maven/Gradle's `20` / `20` is rejected because 
it is unlabeled-graph compaction, while Spring's labeled graphs use `40` / 
`80`; `40` / `60` is a moderate bump for typical  [...]
+
+**Tech Stack:** IntelliJ IDEA Ultimate 2026.2 (262), `com.intellij.diagram` 
(`DiagramExtras#getCustomLayouter`), `com.intellij.openapi.graph` 
(`HierarchicGroupLayouter` / `HierarchicLayouter` distance setters), JUnit 4 
light tests (`BasicLightHighlightingTestCase`).
+
+**Spec:** 
`docs/superpowers/specs/2026-07-26-show-diagram-result-edge-label-spacing-design.md`
+
+## Global Constraints
+
+- Target platform remains IntelliJ IDEA **2026.2** / build **262** only 
(`pluginSinceBuild=262`, `pluginUntilBuild=262.*`).
+- Hard dependency on plugin id **`com.intellij.diagram`** already present — do 
not change dependency shape.
+- Touch **Show Diagram custom layouter extras only** — do **not** modify or 
remove the Swing Diagram tab (`diagram.fileEditor` / `diagram.ui`).
+- Do **not** change `StrutsConfigDiagramModel` semantics, 
`StrutsDiagramApiEdge` relationship Builder, Dom refresh, compact node chrome, 
navigation, or tooltips.
+- Keep orientation **`LEFT_TO_RIGHT`**. Soft preference = do not reset a 
user-selected non-custom toolbar layout via `GraphSettings.setCurrentLayouter`.
+- Platform defaults are node **`20.0`** and layer **`40.0`** on IU 2026.2.
+- Production spacing values are exactly node **`40.0`** and layer **`60.0`**, 
raising both above their defaults.
+- Maven/Gradle **`20` / `20`** is not the precedent: it compacts unlabeled 
graphs and would leave node spacing unchanged while halving layer spacing.
+- Do **not** call `createBFSLayerer()` / `setLayerer(...)`.
+- Do **not** change edge label model / upper-center placement.
+- Do **not** apply spacing to non-custom toolbar layouts.
+- Do **not** implement #96–#100 or Swing tab removal in this plan.
+- Tests gate: `./gradlew test -x rat --tests "com.intellij.struts2.diagram.*"`.
+- Learn API details from local IU SDK jars under Gradle caches 
(`lib/intellij.platform.graph.jar`, `plugins/uml/lib/uml-support.jar`) if 
signatures drift.
+
+---
+
+## File Structure
+
+| File | Action | Responsibility |
+|---|---|---|
+| 
`src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java` 
| Modify | After LTR, set minimal node/layer distances to `40.0` / `60.0` |
+| `src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java` 
| Modify | Assert custom layouter distances are `40.0` / `60.0` |
+| `CHANGELOG.md` | Modify | Unreleased note for Show Diagram label spacing 
(#128) |
+
+No new production classes. No `plugin.xml` / Gradle dependency changes. No Dom 
refresh changes (verify no `setCurrentLayouter`).
+
+---
+
+### Task 1: Failing test for above-default layouter distances
+
+**Files:**
+- Modify: 
`src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java`
+- Test: 
`src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java`
+
+**Interfaces:**
+- Consumes:
+  - `StrutsDiagramProvider.getExtras()`
+  - `DiagramExtras.getCustomLayouter(GraphSettings, Project)`
+  - `com.intellij.openapi.graph.settings.GraphSettings`
+  - 
`com.intellij.openapi.graph.layout.CanonicMultiStageLayouter#getLayoutOrientation()`
+  - `com.intellij.openapi.graph.layout.LayoutOrientation#LEFT_TO_RIGHT`
+  - `com.intellij.openapi.graph.layout.hierarchic.HierarchicGroupLayouter`
+  - 
`com.intellij.openapi.graph.layout.hierarchic.HierarchicLayouter#getMinimalNodeDistance()`
+  - 
`com.intellij.openapi.graph.layout.hierarchic.HierarchicLayouter#getMinimalLayerDistance()`
+- Produces: failing assertions that custom layouter node/layer distances equal 
`40.0` / `60.0` (current implementation uses `20.0` / `20.0`)
+
+- [ ] **Step 1: Extend `testCustomLayouterIsHierarchicLeftToRight` with 
distance asserts**
+
+In 
`src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java`, 
replace the existing `testCustomLayouterIsHierarchicLeftToRight` method with:
+
+```java
+    public void testCustomLayouterIsHierarchicLeftToRight() {
+        StrutsDiagramProvider provider = getProvider();
+        DiagramExtras<StrutsDiagramItem> extras = provider.getExtras();
+        assertFalse("Custom layouter is only consulted when 
useDefaultLayouter() is false",
+                extras.useDefaultLayouter());
+        GraphSettings settings = new GraphSettings();
+
+        Layouter layouter = extras.getCustomLayouter(settings, getProject());
+        assertNotNull("Show Diagram must provide a custom layouter for LTR 
hierarchy", layouter);
+        assertInstanceOf(layouter, HierarchicGroupLayouter.class);
+
+        HierarchicGroupLayouter hierarchic = (HierarchicGroupLayouter) 
layouter;
+        CanonicMultiStageLayouter multiStage = (CanonicMultiStageLayouter) 
layouter;
+        assertEquals("Custom layouter must be left-to-right (Maven/Gradle 
pattern)",
+                LayoutOrientation.LEFT_TO_RIGHT,
+                multiStage.getLayoutOrientation());
+        assertEquals("Custom layouter must leave more vertical room for result 
labels",
+                40.0,
+                hierarchic.getMinimalNodeDistance(),
+                0.0);
+        assertEquals("Custom layouter must leave more horizontal room for 
result labels",
+                60.0,
+                hierarchic.getMinimalLayerDistance(),
+                0.0);
+
+        // Stable custom path — soft preference is "don't reset 
GraphSettings", not reading orientation
+        Layouter again = extras.getCustomLayouter(settings, getProject());
+        assertNotNull(again);
+        assertEquals(LayoutOrientation.LEFT_TO_RIGHT,
+                ((CanonicMultiStageLayouter) again).getLayoutOrientation());
+        HierarchicGroupLayouter againHierarchic = (HierarchicGroupLayouter) 
again;
+        assertEquals("Repeated custom layouter must preserve minimal node 
distance",
+                40.0, againHierarchic.getMinimalNodeDistance(), 0.0);
+        assertEquals("Repeated custom layouter must preserve minimal layer 
distance",
+                60.0, againHierarchic.getMinimalLayerDistance(), 0.0);
+    }
+```
+
+Notes:
+
+- Keep existing imports (`CanonicMultiStageLayouter`, `Layouter`, 
`LayoutOrientation`, `HierarchicGroupLayouter`, `GraphSettings`). No new 
imports required if those are already present.
+- Do **not** assert pixel coordinates, label bounding boxes, or rendered 
overlap.
+- Use the three-argument double overload with a message: 
`assertEquals(message, expected, actual, 0.0)`.
+
+- [ ] **Step 2: Run test to verify it fails**
+
+Run:
+
+```bash
+./gradlew test -x rat --tests 
"com.intellij.struts2.diagram.StrutsDiagramProviderTest.testCustomLayouterIsHierarchicLeftToRight"
+```
+
+Expected: FAIL because current `getCustomLayouter` still sets node/layer 
distances to `20.0` / `20.0`.
+
+- [ ] **Step 3: Commit the failing test**
+
+```bash
+git add 
src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
+git commit -m "$(cat <<'EOF'
+test(diagram): require above-default Show Diagram layouter distances (#128)
+
+EOF
+)"
+```
+
+---
+
+### Task 2: Set above-default distances on custom LTR layouter
+
+**Files:**
+- Modify: 
`src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java`
+- Test: 
`src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java`
+
+**Interfaces:**
+- Consumes:
+  - `GraphManager.getGraphManager()`
+  - `GraphManager.createHierarchicGroupLayouter()`
+  - `GraphManager.createOrientationLayouter(byte)`
+  - `HierarchicGroupLayouter.setOrientationLayouter(LayoutStage)`
+  - `HierarchicLayouter.setMinimalNodeDistance(double)`
+  - `HierarchicLayouter.setMinimalLayerDistance(double)`
+  - `LayoutOrientation.LEFT_TO_RIGHT`
+- Produces: `StrutsDiagramExtras.getCustomLayouter(GraphSettings, Project)` 
returning hierarchic LTR with node/layer distances `40.0` / `60.0`
+
+- [ ] **Step 1: Update `getCustomLayouter` in `StrutsDiagramExtras`**
+
+Replace the existing `getCustomLayouter` method body so the method looks like 
this (keep the existing javadoc intent; expand slightly if useful):
+
+```java
+    /**
+     * Prefer package → action → result left-to-right on Show Diagram.
+     * {@code settings} is deliberately not consulted: the platform default 
orientation is
+     * top-to-bottom, and the soft preference on Dom refresh is not resetting 
the user's
+     * toolbar layout choice rather than reading orientation from {@link 
GraphSettings}.
+     * <p>
+     * Distances above the platform defaults give multi-result edge labels 
more vertical
+     * and horizontal room under LTR hierarchy.
+     */
+    @Override
+    public @NotNull Layouter getCustomLayouter(GraphSettings settings,
+                                               Project project) {
+        GraphManager graphManager = GraphManager.getGraphManager();
+        HierarchicGroupLayouter layouter = 
graphManager.createHierarchicGroupLayouter();
+        layouter.setOrientationLayouter(
+                
graphManager.createOrientationLayouter(LayoutOrientation.LEFT_TO_RIGHT));
+        layouter.setMinimalNodeDistance(40.0);
+        layouter.setMinimalLayerDistance(60.0);
+        return layouter;
+    }
+```
+
+Notes for the implementer:
+
+- Use `40.0` / `60.0`, above the IU 2026.2 defaults (`20.0` / `40.0`).
+- Do not copy Maven/Gradle's `20` / `20` unlabeled-compaction values. Spring's 
`40` / `80` is only a reference point; this plan deliberately uses the moderate 
`40` / `60` bump.
+- Do **not** call `graphManager.createBFSLayerer()` or 
`layouter.setLayerer(...)`.
+- Do **not** call `settings.setCurrentLayouter(...)`.
+- Do **not** read `settings.getCurrentLayoutOrientation()`.
+- Do **not** override `doEdgeLabeling()` — leave platform default (`true`).
+- Keep parameter nullability matching the current override signature in this 
file (drop `@NotNull` on parameters/return only if the platform signature 
requires it).
+- Confirm setters exist on `HierarchicGroupLayouter` / `HierarchicLayouter` if 
the IDE complains:
+
+```bash
+GRAPH_JAR=$(find ~/.gradle/caches -path 
'*idea-2026.2*/lib/intellij.platform.graph.jar' | head -1)
+javap -classpath "$GRAPH_JAR" -public 
com.intellij.openapi.graph.layout.hierarchic.HierarchicLayouter | rg Minimal
+```
+
+- [ ] **Step 2: Run the focused test to verify it passes**
+
+Run:
+
+```bash
+./gradlew test -x rat --tests 
"com.intellij.struts2.diagram.StrutsDiagramProviderTest.testCustomLayouterIsHierarchicLeftToRight"
+```
+
+Expected: PASS.
+
+- [ ] **Step 3: Run the diagram test suite**
+
+Run:
+
+```bash
+./gradlew test -x rat --tests "com.intellij.struts2.diagram.*"
+```
+
+Expected: BUILD SUCCESSFUL; all `com.intellij.struts2.diagram.*` tests green.
+
+- [ ] **Step 4: Confirm Dom refresh still does not reset layouter**
+
+Run:
+
+```bash
+rg -n "setCurrentLayouter" src/main/java/com/intellij/struts2/diagram
+```
+
+Expected: no matches (soft preference preserved by omission).
+
+- [ ] **Step 5: Confirm BFS layerer was not introduced**
+
+Run:
+
+```bash
+rg -n "createBFSLayerer|setLayerer" src/main/java/com/intellij/struts2/diagram
+```
+
+Expected: no matches.
+
+- [ ] **Step 6: Commit the implementation**
+
+```bash
+git add 
src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java \
+        
src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
+git commit -m "$(cat <<'EOF'
+fix(diagram): space Show Diagram result edge labels in LTR (#128)
+
+EOF
+)"
+```
+
+---
+
+### Task 3: Changelog + manual smoke checklist
+
+**Files:**
+- Modify: `CHANGELOG.md`
+
+**Interfaces:**
+- Consumes: Unreleased changelog sections already used by prior Show Diagram 
entries
+- Produces: Unreleased Fixed (or Changed) bullet linking #128
+
+- [ ] **Step 1: Add Unreleased changelog entry**
+
+Under `## [Unreleased]` → `### Fixed` in `CHANGELOG.md`, add (keep existing 
bullets; place near other Show Diagram fixes):
+
+```markdown
+- Fix Show Diagram cramped multi-result edge labels under left-to-right layout 
([#128](https://github.com/apache/struts-intellij-plugin/issues/128))
+```
+
+If `### Fixed` is missing under Unreleased, create that subsection rather than 
putting the bullet under `### Changed`.
+
+- [ ] **Step 2: Manual smoke (`./gradlew runIde` on IU)**
+
+1. Open a Struts config whose action has at least two named results (e.g. 
`success` and `delete`).
+2. Show Diagram → confirm package → action → result still reads left-to-right.
+3. Confirm result edge labels are readable without overlapping each other.
+4. Confirm labels do not collide with the action node chrome.
+5. Optionally pick a non-custom toolbar layout, edit XML / refresh → confirm 
the user’s layout choice is not forced back to custom LTR (soft preference from 
#122).
+
+- [ ] **Step 3: Commit changelog**
+
+```bash
+git add CHANGELOG.md
+git commit -m "$(cat <<'EOF'
+docs: note Show Diagram result edge label spacing (#128)
+
+EOF
+)"
+```
+
+---
+
+## Spec coverage checklist
+
+| Spec requirement | Task |
+|---|---|
+| `setMinimalNodeDistance(40.0)` + `setMinimalLayerDistance(60.0)` on custom 
LTR layouter | Task 2 |
+| Keep LTR orientation / #122 flow | Task 2 (preserve existing orientation) + 
Task 3 manual |
+| Upper-center labels / relationship Builder unchanged | Global constraint + 
no ApiEdge edits |
+| No BFS layerer | Task 2 notes + Step 5 grep |
+| Custom LTR path only; other toolbar layouts untouched | Global constraint + 
soft-preference grep |
+| Soft preference / no `setCurrentLayouter` | Task 2 Step 4 |
+| Automated distance asserts | Task 1 |
+| Manual `runIde` multi-result smoke | Task 3 |
+| Changelog #128 | Task 3 |
+| No model / Dom refresh / Swing / pixel asserts | Global constraints |
+
+## Plan self-review
+
+1. **Spec coverage:** All goals/decisions from the design map to Task 1–3; 
future work (label model, BFS, non-custom layouts) stays out.
+2. **Placeholder scan:** No TBD/TODO; concrete code, commands, and expected 
results included.
+3. **Type consistency:** `HierarchicGroupLayouter` + `getMinimalNodeDistance` 
/ `getMinimalLayerDistance` / setters use `40.0` / `60.0` consistently; method 
name remains `getCustomLayouter(GraphSettings, Project)`.
diff --git 
a/docs/superpowers/specs/2026-07-26-show-diagram-result-edge-label-spacing-design.md
 
b/docs/superpowers/specs/2026-07-26-show-diagram-result-edge-label-spacing-design.md
new file mode 100644
index 0000000..e4dd68a
--- /dev/null
+++ 
b/docs/superpowers/specs/2026-07-26-show-diagram-result-edge-label-spacing-design.md
@@ -0,0 +1,160 @@
+# Show Diagram: Result Edge Label Spacing in LTR Layout
+
+**Date:** 2026-07-26  
+**Status:** Approved for implementation planning  
+**Related:** 
[#128](https://github.com/apache/struts-intellij-plugin/issues/128); follow-up 
to [#122](https://github.com/apache/struts-intellij-plugin/issues/122) / 
[#124](https://github.com/apache/struts-intellij-plugin/pull/124)
+
+## Problem
+
+After Show Diagram switched to a custom left-to-right hierarchic layouter 
(#122 / #124), typical package → action → result flow reads correctly. Manual 
smoke found that when one action has multiple results (e.g. `success` and 
`delete`), the action → result edge labels sit on top of each other and too 
close to the action node chrome, so they are hard to read.
+
+Issue [#128](https://github.com/apache/struts-intellij-plugin/issues/128) 
frames this as edge-label placement / layouter spacing, not model semantics. 
The LTR plan deferred Maven-style distance tweaks as YAGNI unless readability 
suffered; this issue is that follow-up.
+
+## Goals
+
+1. Multiple result edge labels from one action are readable without 
overlapping each other.
+2. Labels do not collide with the action node chrome.
+3. LTR package → action → result flow from #122 remains intact.
+4. Upper-center edge labels and relationship styling (including arrowheads 
from #125) stay as they are.
+5. Automated coverage for the custom layouter distance settings, plus a short 
manual `runIde` check.
+6. Changelog documents the Show Diagram label-spacing fix.
+
+## Non-Goals
+
+- Changing edge label model / placement (upper-center stays).
+- Adopting Maven/Gradle `createBFSLayerer()` (layer assignment, not label 
spacing).
+- Tuning spacing for non-custom toolbar layouts (Organic, platform Hierarchic, 
etc.).
+- Generous spacing for dense 4–5+ result actions beyond a minimal 2–3 result 
fix.
+- Model semantics, Dom refresh, compact node chrome, navigation, tooltips.
+- Swing Diagram tab pixel parity or Robot / UI e2e.
+- Pixel/layout geometry assertions.
+
+## Decisions
+
+| Question | Decision |
+|---|---|
+| Primary lever | Layouter spacing only (`setMinimalNodeDistance` / 
`setMinimalLayerDistance`) |
+| Platform defaults | Node `20.0`; layer `40.0` on IU 2026.2 |
+| Chosen values | Node `40.0`; layer `60.0` — both above platform defaults |
+| Label model | Unchanged (keep upper-center labels) |
+| Scope of tweak | Custom LTR layouter from `getCustomLayouter` only |
+| BFS layerer | Out of scope |
+| Soft layout preference (#122) | Unchanged — do not mutate `GraphSettings` on 
Dom refresh |
+| Verification | Unit asserts on layouter distances + manual `runIde` |
+
+### Alternatives considered
+
+| Approach | Verdict |
+|---|---|
+| `setMinimalNodeDistance(40.0)` + `setMinimalLayerDistance(60.0)` on the 
custom LTR layouter; no label-model change; skip BFS | **Chosen** — a moderate 
bump above the IU 2026.2 defaults for typical actions with 2–3 results |
+| Maven/Gradle distances (`20` / `20`) | Rejected — their unlabeled graph 
compaction is the wrong precedent; node `20` is a no-op versus the platform 
default and layer `20` halves the default `40` |
+| Spring labeled-graph distances (`40` / `80`) | Rejected as unnecessarily 
generous for typical 2–3 result actions; `40` / `60` retains the useful node 
bump with a moderate layer bump |
+| Node distance only | Rejected — may fix label-vs-label overlap but leave 
labels cramped against the action node |
+| Full Maven extras parity including `createBFSLayerer()` | Rejected — more 
behavioral change than needed for label spacing |
+| Label placement / model changes first | Rejected — user chose spacing-only |
+| Apply spacing to other toolbar layouts | Rejected — custom LTR path only |
+
+## Architecture
+
+Root cause is insufficient hierarchic spacing on the custom LTR layouter path. 
Snapshot edges, relationship Builder (solid + `ANGLE` + upper-center label), 
and `doEdgeLabeling` remain correct. IU 2026.2 defaults to node distance `20.0` 
and layer distance `40.0`; the custom path raises these to `40.0` and `60.0`.
+
+```
+StrutsDiagramProvider
+  └── StrutsDiagramExtras  (CommonDiagramExtras)
+        └── getCustomLayouter(settings, project)
+              ├── GraphManager.createHierarchicGroupLayouter()
+              ├── OrientationLayouter = LEFT_TO_RIGHT
+              ├── setMinimalNodeDistance(40.0)
+              └── setMinimalLayerDistance(60.0)
+```
+
+**Why both distances:** In LTR hierarchy, results stack in the same layer 
(vertical separation → node distance), while action and result sit in adjacent 
layers (horizontal gap → layer distance). Raising both above their platform 
defaults gives labels more vertical and horizontal room. Spring labeled graphs 
use `40` / `80`; `40` / `60` is a moderate bump for typical 2–3 result actions.
+
+**Unchanged:** `StrutsConfigDiagramModel`, `StrutsDiagramDataModel`, 
`StrutsDiagramApiEdge`, Dom refresh, compact label chrome, Swing `fileEditor` / 
`ui`, `plugin.xml`. Soft preference from #122 remains: Dom refresh must not 
call `GraphSettings.setCurrentLayouter`.
+
+## Components
+
+| Unit | Role |
+|---|---|
+| `StrutsDiagramExtras` | Only production change: after LTR orientation, set 
minimal node/layer distances to `40.0` / `60.0` |
+| `StrutsDiagramProviderTest` | Extend custom-layouter test to assert 
node/layer distances `40.0` / `60.0` (keep LTR assertion) |
+| Snapshot / API edge / Dom refresh / Swing | Untouched |
+
+No new production classes. No `plugin.xml` changes for this issue.
+
+## Data flow
+
+### Initial open / CUSTOM path
+
+1. User invokes Show Diagram on a Struts 2 config.
+2. Platform asks extras for the custom layouter.
+3. Extras returns a hierarchic group layouter oriented `LEFT_TO_RIGHT` with 
node/layer distances `40.0` / `60.0`.
+4. Graph lays out with LTR flow; edge labeling remains enabled; multi-result 
labels have more room.
+
+### After user picks another layout algorithm
+
+1. User selects a non-custom layout from the diagram toolbar.
+2. Platform stores that layouter on `GraphSettings`.
+3. Dom refresh must not force spacing or custom LTR back onto that choice.
+4. Spacing tweaks apply only when the custom layouter path is active.
+
+## Error handling & edge cases
+
+| Scenario | Behavior |
+|---|---|
+| Action with 2–3 results (e.g. `success` + `delete`) | Labels readable; no 
mutual overlap; clear of action chrome (manual smoke) |
+| Action with a single result | Unchanged readability; slightly roomier LTR 
graph |
+| Dense 4–5+ result actions | Best-effort only; not the acceptance bar for 
this issue |
+| User-selected Organic / platform Hierarchic | Platform defaults; our 
distances do not apply |
+| Soft preference / Dom refresh | Non-custom toolbar layout choice preserved |
+| Empty / unavailable model | Unchanged — no graph to lay out |
+| Swing Diagram tab | Unchanged |
+
+## Testing
+
+### Automated
+
+Extend `StrutsDiagramProviderTest.testCustomLayouterIsHierarchicLeftToRight` 
(or a focused sibling):
+
+1. Call `StrutsDiagramExtras.getCustomLayouter(...)` → assert hierarchic 
layouter oriented `LEFT_TO_RIGHT`.
+2. Assert `getMinimalNodeDistance() == 40.0` and `getMinimalLayerDistance() == 
60.0`.
+3. Do not assert pixel coordinates, label bounding boxes, or rendered overlap.
+
+Gate: `./gradlew test -x rat --tests "com.intellij.struts2.diagram.*"`
+
+### Manual (`./gradlew runIde` on IU)
+
+1. Show Diagram on a Struts config whose action has at least two named results 
(e.g. `success` and `delete`).
+2. Confirm result edge labels are readable without overlapping each other.
+3. Confirm labels do not collide with the action node chrome.
+4. Confirm package → action → result still reads left-to-right.
+
+### Out of scope
+
+- Pixel/layout assertions
+- Robot / UI e2e
+- Swing tab removal
+- Tests for #96–#100
+
+## Future work (out of scope)
+
+- Label-model / placement tweaks if spacing alone proves insufficient after 
smoke
+- BFS layerer or other Maven extras parity
+- Spacing for non-custom toolbar layouts
+- Remove Swing Diagram tab once Show Diagram UX is solid
+- [#96](https://github.com/apache/struts-intellij-plugin/issues/96) merged 
multi-file view
+- [#98](https://github.com/apache/struts-intellij-plugin/issues/98) selection 
sync
+- [#99](https://github.com/apache/struts-intellij-plugin/issues/99) Structure 
tool window
+- [#100](https://github.com/apache/struts-intellij-plugin/issues/100) zoom/pan 
beyond platform chrome
+
+## References
+
+- Issue [#128](https://github.com/apache/struts-intellij-plugin/issues/128)
+- Prior LTR layout design: 
`docs/superpowers/specs/2026-07-26-show-diagram-ltr-layout-design.md`
+- Prior arrowheads design: 
`docs/superpowers/specs/2026-07-26-show-diagram-result-edge-arrowheads-design.md`
+- `com.intellij.struts2.diagram.provider.StrutsDiagramExtras`
+- 
`com.intellij.openapi.graph.layout.hierarchic.HierarchicLayouter#setMinimalNodeDistance`
+- 
`com.intellij.openapi.graph.layout.hierarchic.HierarchicLayouter#setMinimalLayerDistance`
+- IU 2026.2 `HierarchicLayouter` defaults: node `20.0`, layer `40.0`
+- Maven/Gradle UML extras `getCustomLayouter` (`20` / `20`; rejected 
unlabeled-compaction precedent)
+- Spring labeled-graph extras (`40` / `80`; reference point for a more 
generous spacing policy)
diff --git 
a/src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java 
b/src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java
index 0f4292d..4d54ff4 100644
--- 
a/src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java
+++ 
b/src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java
@@ -71,6 +71,9 @@ public final class StrutsDiagramExtras extends 
CommonDiagramExtras<StrutsDiagram
      * {@code settings} is deliberately not consulted: the platform default 
orientation is
      * top-to-bottom, and the soft preference on Dom refresh is not resetting 
the user's
      * toolbar layout choice rather than reading orientation from {@link 
GraphSettings}.
+     * <p>
+     * Distances above the platform defaults give multi-result edge labels 
more vertical
+     * and horizontal room under LTR hierarchy.
      */
     @Override
     public @NotNull Layouter getCustomLayouter(GraphSettings settings,
@@ -79,6 +82,8 @@ public final class StrutsDiagramExtras extends 
CommonDiagramExtras<StrutsDiagram
         HierarchicGroupLayouter layouter = 
graphManager.createHierarchicGroupLayouter();
         layouter.setOrientationLayouter(
                 
graphManager.createOrientationLayouter(LayoutOrientation.LEFT_TO_RIGHT));
+        layouter.setMinimalNodeDistance(40.0);
+        layouter.setMinimalLayerDistance(60.0);
         return layouter;
     }
 
diff --git 
a/src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java 
b/src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
index 324297d..eebfaa0 100644
--- a/src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
+++ b/src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
@@ -217,16 +217,30 @@ public class StrutsDiagramProviderTest extends 
BasicLightHighlightingTestCase {
         assertNotNull("Show Diagram must provide a custom layouter for LTR 
hierarchy", layouter);
         assertInstanceOf(layouter, HierarchicGroupLayouter.class);
 
+        HierarchicGroupLayouter hierarchic = (HierarchicGroupLayouter) 
layouter;
         CanonicMultiStageLayouter multiStage = (CanonicMultiStageLayouter) 
layouter;
         assertEquals("Custom layouter must be left-to-right (Maven/Gradle 
pattern)",
                 LayoutOrientation.LEFT_TO_RIGHT,
                 multiStage.getLayoutOrientation());
+        assertEquals("Custom layouter must leave more vertical room for result 
labels",
+                40.0,
+                hierarchic.getMinimalNodeDistance(),
+                0.0);
+        assertEquals("Custom layouter must leave more horizontal room for 
result labels",
+                60.0,
+                hierarchic.getMinimalLayerDistance(),
+                0.0);
 
         // Stable custom path — soft preference is "don't reset 
GraphSettings", not reading orientation
         Layouter again = extras.getCustomLayouter(settings, getProject());
         assertNotNull(again);
         assertEquals(LayoutOrientation.LEFT_TO_RIGHT,
                 ((CanonicMultiStageLayouter) again).getLayoutOrientation());
+        HierarchicGroupLayouter againHierarchic = (HierarchicGroupLayouter) 
again;
+        assertEquals("Repeated custom layouter must preserve minimal node 
distance",
+                40.0, againHierarchic.getMinimalNodeDistance(), 0.0);
+        assertEquals("Repeated custom layouter must preserve minimal layer 
distance",
+                60.0, againHierarchic.getMinimalLayerDistance(), 0.0);
     }
 
     /**

Reply via email to