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);
}
/**