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 d9751d7  Show Diagram: left-to-right hierarchic layout (#122) (#124)
d9751d7 is described below

commit d9751d79a0a526f4085110ea7f1175a98bc73ab4
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sun Jul 26 05:05:34 2026 +0200

    Show Diagram: left-to-right hierarchic layout (#122) (#124)
    
    * docs: design Show Diagram left-to-right layout (#122)
    
    Capture the refreshed #122 approach: Maven-style custom hierarchic
    LTR layouter with soft preference for user-selected layout algorithms.
    
    Co-authored-by: Cursor <[email protected]>
    
    * docs: plan Show Diagram left-to-right layout (#122)
    
    Co-authored-by: Cursor <[email protected]>
    
    * test(diagram): require Show Diagram LTR custom layouter (#122)
    
    Co-authored-by: Cursor <[email protected]>
    
    * feat(diagram): use LTR hierarchic Show Diagram layouter (#122)
    
    Co-authored-by: Cursor <[email protected]>
    
    * docs: changelog Show Diagram LTR layout (#122)
    
    Co-authored-by: Cursor <[email protected]>
    
    * test(diagram): assert useDefaultLayouter stays false for LTR (#122)
    
    Co-authored-by: Cursor <[email protected]>
    
    ---------
    
    Co-authored-by: Cursor <[email protected]>
---
 CHANGELOG.md                                       |   1 +
 .../plans/2026-07-26-show-diagram-ltr-layout.md    | 309 +++++++++++++++++++++
 .../2026-07-26-show-diagram-ltr-layout-design.md   | 162 +++++++++++
 .../diagram/provider/StrutsDiagramExtras.java      |  22 ++
 .../struts2/diagram/StrutsDiagramProviderTest.java |  28 ++
 5 files changed, 522 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 221d3b3..8e0fb18 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@
 - Dependencies - declare IntelliJ Platform 2026.2 v2 content modules 
`intellij.javascript.parser`, `intellij.javascript.backend`, 
`intellij.javaee.jsp.shared`, `intellij.css`, and `intellij.css.backend` 
([#103](https://github.com/apache/struts-intellij-plugin/issues/103))
 - Add IntelliJ **Show Diagram** support for Struts 2 configs via 
`com.intellij.diagram`, alongside the existing Diagram editor tab for 
comparison ([#117](https://github.com/apache/struts-intellij-plugin/issues/117))
 - Show Diagram Struts config nodes use compact icon+label chrome instead of 
empty UML class boxes 
([#120](https://github.com/apache/struts-intellij-plugin/issues/120))
+- Show Diagram uses a left-to-right hierarchic layout by default for Struts 
config graphs 
([#122](https://github.com/apache/struts-intellij-plugin/issues/122))
 - Dependencies - hard-depend on `com.intellij.diagram` (Ultimate Diagrams)
 
 ## [261.19027.1] - 2026-07-13
diff --git a/docs/superpowers/plans/2026-07-26-show-diagram-ltr-layout.md 
b/docs/superpowers/plans/2026-07-26-show-diagram-ltr-layout.md
new file mode 100644
index 0000000..dcdd87b
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-26-show-diagram-ltr-layout.md
@@ -0,0 +1,309 @@
+# Show Diagram Left-to-Right Layout 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:** Make IntelliJ Show Diagram for Struts configs use a hierarchic 
left-to-right custom layouter by default so package → action → result reads LTR.
+
+**Architecture:** Keep the existing Show Diagram provider/data model. Override 
only `StrutsDiagramExtras.getCustomLayouter` to return a Maven/Gradle-style 
hierarchic group layouter oriented `LEFT_TO_RIGHT`. Soft preference is already 
satisfied because Dom refresh does not call `GraphSettings.setCurrentLayouter` 
— do not add that. Leave Swing Diagram tab and snapshot model untouched.
+
+**Tech Stack:** IntelliJ IDEA Ultimate 2026.2 (262), `com.intellij.diagram` 
(`DiagramExtras#getCustomLayouter`), `com.intellij.openapi.graph` 
(`GraphManager`, `HierarchicGroupLayouter`, `LayoutOrientation`), JUnit 4 light 
tests (`BasicLightHighlightingTestCase`).
+
+**Spec:** `docs/superpowers/specs/2026-07-26-show-diagram-ltr-layout-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 layouter extras only** — do **not** modify or remove 
the Swing Diagram tab (`diagram.fileEditor` / `diagram.ui`).
+- Do **not** change `StrutsConfigDiagramModel` semantics, edge mapping, 
tooltips, navigation, compact node chrome, or Dom refresh logic.
+- Custom layouter orientation is always **`LEFT_TO_RIGHT`** (Maven pattern). 
Soft preference = do not reset a user-selected non-custom toolbar layout via 
`GraphSettings.setCurrentLayouter`.
+- Do **not** read `GraphSettings.getCurrentLayoutOrientation()` for the custom 
layouter (platform default is top-to-bottom and would defeat LTR).
+- 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 | Override `getCustomLayouter` → hierarchic LTR |
+| `src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java` 
| Modify | Assert custom layouter is hierarchic and `LEFT_TO_RIGHT` |
+| `CHANGELOG.md` | Modify | Unreleased note for Show Diagram LTR layout (#122) 
|
+
+No new production classes. No `plugin.xml` / Gradle dependency changes. No Dom 
refresh changes (verify no `setCurrentLayouter`).
+
+---
+
+### Task 1: Failing test for LTR custom layouter
+
+**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`
+- Produces: failing assertion that `getCustomLayouter` returns hierarchic LTR 
(currently returns `null` from `DiagramExtras` default)
+
+- [ ] **Step 1: Add imports to `StrutsDiagramProviderTest`**
+
+Add these imports (keep existing ones):
+
+```java
+import com.intellij.openapi.graph.layout.CanonicMultiStageLayouter;
+import com.intellij.openapi.graph.layout.Layouter;
+import com.intellij.openapi.graph.layout.LayoutOrientation;
+import com.intellij.openapi.graph.layout.hierarchic.HierarchicGroupLayouter;
+import com.intellij.openapi.graph.settings.GraphSettings;
+```
+
+- [ ] **Step 2: Add failing test method**
+
+Add this test method to `StrutsDiagramProviderTest` (near the other extras 
tests):
+
+```java
+    public void testCustomLayouterIsHierarchicLeftToRight() {
+        StrutsDiagramProvider provider = getProvider();
+        DiagramExtras<StrutsDiagramItem> extras = provider.getExtras();
+        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);
+
+        CanonicMultiStageLayouter multiStage = (CanonicMultiStageLayouter) 
layouter;
+        assertEquals("Custom layouter must be left-to-right (Maven/Gradle 
pattern)",
+                LayoutOrientation.LEFT_TO_RIGHT,
+                multiStage.getLayoutOrientation());
+
+        // 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());
+    }
+```
+
+- [ ] **Step 3: Run test to verify it fails**
+
+Run:
+
+```bash
+./gradlew test -x rat --tests 
"com.intellij.struts2.diagram.StrutsDiagramProviderTest.testCustomLayouterIsHierarchicLeftToRight"
+```
+
+Expected: FAIL because current `DiagramExtras.getCustomLayouter` returns 
`null` (assertion `assertNotNull(...)` fails).
+
+- [ ] **Step 4: Commit the failing test**
+
+```bash
+git add 
src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
+git commit -m "$(cat <<'EOF'
+test(diagram): require Show Diagram LTR custom layouter (#122)
+
+EOF
+)"
+```
+
+---
+
+### Task 2: Implement Maven-style LTR `getCustomLayouter`
+
+**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)`
+  - `LayoutOrientation.LEFT_TO_RIGHT`
+- Produces: `StrutsDiagramExtras.getCustomLayouter(GraphSettings, Project)` 
returning hierarchic LTR
+
+- [ ] **Step 1: Add imports to `StrutsDiagramExtras`**
+
+Add these imports (keep existing ones):
+
+```java
+import com.intellij.openapi.graph.GraphManager;
+import com.intellij.openapi.graph.layout.Layouter;
+import com.intellij.openapi.graph.layout.LayoutOrientation;
+import com.intellij.openapi.graph.layout.hierarchic.HierarchicGroupLayouter;
+import com.intellij.openapi.graph.settings.GraphSettings;
+import com.intellij.openapi.project.Project;
+```
+
+- [ ] **Step 2: Override `getCustomLayouter`**
+
+Add this method to `StrutsDiagramExtras` (after `isZoomAnimationsEnabled()` is 
a good place):
+
+```java
+    /**
+     * Prefer package → action → result left-to-right on Show Diagram.
+     * Soft preference for user-selected non-custom toolbar layouts is handled 
by
+     * not mutating {@link GraphSettings#setCurrentLayouter} on Dom refresh.
+     */
+    @Override
+    public @NotNull Layouter getCustomLayouter(@NotNull GraphSettings settings,
+                                               @NotNull Project project) {
+        GraphManager graphManager = GraphManager.getGraphManager();
+        HierarchicGroupLayouter layouter = 
graphManager.createHierarchicGroupLayouter();
+        layouter.setOrientationLayouter(
+                
graphManager.createOrientationLayouter(LayoutOrientation.LEFT_TO_RIGHT));
+        return layouter;
+    }
+```
+
+Notes for the implementer:
+
+- Match the Maven/Gradle UML extras pattern (`createHierarchicGroupLayouter` + 
`createOrientationLayouter(LEFT_TO_RIGHT)`).
+- Do **not** call `settings.setCurrentLayouter(...)`.
+- Do **not** read `settings.getCurrentLayoutOrientation()`.
+- Optional Maven tweaks (`setMinimalNodeDistance`, 
`setLayerer(createBFSLayerer())`) are **out of scope** unless the minimal 
override fails manual LTR smoke — YAGNI.
+- If `@NotNull` on the override conflicts with the platform signature 
(`Layouter` nullable), drop `@NotNull` on the method return and keep `return 
layouter;` non-null in practice.
+- Confirm parameter nullability against `javap` on `DiagramExtras` if the IDE 
complains:
+
+```bash
+UML_JAR=$(find ~/.gradle/caches -path 
'*idea-2026.2*/plugins/uml/lib/uml-support.jar' | head -1)
+javap -classpath "$UML_JAR" -public com.intellij.diagram.extras.DiagramExtras 
| rg getCustomLayouter
+```
+
+- [ ] **Step 3: Run the focused test to verify it passes**
+
+Run:
+
+```bash
+./gradlew test -x rat --tests 
"com.intellij.struts2.diagram.StrutsDiagramProviderTest.testCustomLayouterIsHierarchicLeftToRight"
+```
+
+Expected: PASS.
+
+- [ ] **Step 4: 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 5: 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 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'
+feat(diagram): use LTR hierarchic Show Diagram layouter (#122)
+
+EOF
+)"
+```
+
+---
+
+### Task 3: Changelog + issue framing note
+
+**Files:**
+- Modify: `CHANGELOG.md`
+- Related issue: 
[#122](https://github.com/apache/struts-intellij-plugin/issues/122) (PR 
description / optional issue comment)
+
+**Interfaces:**
+- Consumes: Keep a Changelog `[Unreleased]` → `### Changed` section style 
already used for #117/#120
+- Produces: Unreleased changelog bullet for #122
+
+- [ ] **Step 1: Add Unreleased changelog entry**
+
+Under `## [Unreleased]` → `### Changed`, add (near the other Show Diagram 
bullets):
+
+```markdown
+- Show Diagram uses a left-to-right hierarchic layout by default for Struts 
config graphs 
([#122](https://github.com/apache/struts-intellij-plugin/issues/122))
+```
+
+- [ ] **Step 2: Commit changelog**
+
+```bash
+git add CHANGELOG.md
+git commit -m "$(cat <<'EOF'
+docs: changelog Show Diagram LTR layout (#122)
+
+EOF
+)"
+```
+
+- [ ] **Step 3: Manual `runIde` smoke (implementer / reviewer)**
+
+Run:
+
+```bash
+./gradlew runIde
+```
+
+Check on IU:
+
+1. Open a small Struts config → **Show Diagram** → package → action → result 
reads left-to-right.
+2. Edge label (e.g. `success`) remains readable.
+3. Pick a different layout algorithm from the diagram toolbar, edit the XML so 
Dom refresh runs → the user’s layout choice remains (not forced back to custom 
LTR).
+
+- [ ] **Step 4: When opening the PR, refresh #122 framing**
+
+In the PR body (and optionally an issue comment), note:
+
+- Swing Diagram tab LTR is no longer the acceptance baseline.
+- Implementation follows Maven/Gradle `getCustomLayouter` + `LEFT_TO_RIGHT`.
+- Soft preference: Dom refresh does not reset a user-selected non-custom 
layout algorithm.
+
+Suggested PR acceptance checklist:
+
+```markdown
+## Summary
+- Show Diagram Struts configs use hierarchic left-to-right custom layouter via 
`StrutsDiagramExtras.getCustomLayouter`
+- Soft preference: Dom refresh does not reset user-selected non-custom toolbar 
layouts
+- Closes #122
+
+## Test plan
+- [ ] `./gradlew test -x rat --tests "com.intellij.struts2.diagram.*"`
+- [ ] Manual `runIde`: small config reads package → action → result LTR; 
`success` label readable
+- [ ] Manual: change layout algorithm, edit XML → choice preserved after Dom 
refresh
+```
+
+---
+
+## Spec coverage checklist
+
+| Spec requirement | Task |
+|---|---|
+| Initial LTR hierarchic layout via `getCustomLayouter` | Task 2 |
+| Soft preference (no `setCurrentLayouter` on Dom refresh) | Task 2 Step 5 
(verify omission) |
+| Edge labeling left at platform default (`true`) | Task 2 (no 
`doEdgeLabeling` override) |
+| No `StrutsConfigDiagramModel` changes | All tasks (extras/tests/changelog 
only) |
+| Automated orientation smoke test | Task 1 |
+| Manual `runIde` checks | Task 3 Step 3 |
+| Changelog | Task 3 |
+| Update #122 framing | Task 3 Step 4 |
+| No Swing tab / pixel grid / #96–#100 | Global constraints |
+
+## Plan self-review
+
+1. **Spec coverage:** All goals mapped to tasks above; no gaps.
+2. **Placeholder scan:** No TBD/TODO; concrete code and commands included.
+3. **Type consistency:** `getCustomLayouter(GraphSettings, Project) -> 
Layouter` / `HierarchicGroupLayouter` / `LayoutOrientation.LEFT_TO_RIGHT` used 
consistently across tasks.
diff --git 
a/docs/superpowers/specs/2026-07-26-show-diagram-ltr-layout-design.md 
b/docs/superpowers/specs/2026-07-26-show-diagram-ltr-layout-design.md
new file mode 100644
index 0000000..0eec7b1
--- /dev/null
+++ b/docs/superpowers/specs/2026-07-26-show-diagram-ltr-layout-design.md
@@ -0,0 +1,162 @@
+# Show Diagram: Left-to-Right Hierarchical Layout
+
+**Date:** 2026-07-26  
+**Status:** Approved for implementation planning  
+**Related:** 
[#122](https://github.com/apache/struts-intellij-plugin/issues/122); follow-up 
to [#117](https://github.com/apache/struts-intellij-plugin/issues/117) / 
[#119](https://github.com/apache/struts-intellij-plugin/pull/119) and 
[#120](https://github.com/apache/struts-intellij-plugin/issues/120) / 
[#123](https://github.com/apache/struts-intellij-plugin/pull/123)
+
+## Problem
+
+Show Diagram for a Struts config uses the platform default hierarchic 
layouter. For a small file-local graph that often places packages toward the 
bottom and results toward the top, so the package → action → result flow reads 
bottom-up instead of left-to-right.
+
+Issue [#122](https://github.com/apache/struts-intellij-plugin/issues/122) 
correctly states the UX goal, but its framing is partly stale:
+
+- It compares against the custom Swing Diagram tab (`Struts2DiagramComponent`) 
as the layout source of truth. After the Show Diagram migration, that tab is no 
longer the primary UX reference.
+- The cited hooks (`DiagramExtras#getCustomLayouter`, `useDefaultLayouter`) 
remain valid on IntelliJ Platform 2026.2 and are still used by JetBrains 
diagram providers (e.g. Maven/Gradle UML extras).
+
+## Goals
+
+1. Initial Show Diagram layout for typical Struts configs reads left-to-right 
(package → action → result flow).
+2. Soft preference: if the user picks a different layout algorithm via diagram 
chrome, Dom refresh does not reset that choice back to custom LTR.
+3. Edge labels (e.g. `success`) remain readable.
+4. No changes to `StrutsConfigDiagramModel` semantics.
+5. Automated smoke coverage for the extras layouter orientation behavior, plus 
a short manual `runIde` check.
+6. Changelog documents the Show Diagram layout default.
+
+## Non-Goals
+
+- Pixel-perfect three-column grid matching the old Swing host.
+- Removing leftover Swing Diagram tab / `PerspectiveFileEditor` code (separate 
cleanup).
+- Locking orientation so toolbar layout/orientation actions are ignored.
+- Pixel/layout geometry assertions or Robot UI e2e.
+- Changes to compact node chrome (#120), Dom refresh, navigation, or tooltips.
+
+## Decisions
+
+| Question | Decision |
+|---|---|
+| Layout style | Platform hierarchic group layouter oriented left-to-right by 
default |
+| Soft vs fixed | Soft preference at the **layout algorithm** level — custom 
layouter is always hierarchic LTR; do not reset a user-chosen non-custom 
`DiagramLayout` on refresh |
+| Swing tab comparison | Dropped as acceptance baseline; Maven/Gradle 
`getCustomLayouter` pattern is the reference |
+| Model changes | None — extras only |
+| Verification | Unit tests on extras orientation + manual `runIde` |
+
+### Alternatives considered
+
+| Approach | Verdict |
+|---|---|
+| `getCustomLayouter` always hierarchic LTR (Maven/Gradle pattern); soft = 
preserve user-selected alternate `DiagramLayout` | **Chosen** — reliable 
initial LTR; soft without fighting platform default orientation 
(`TOP_TO_BOTTOM`) |
+| `useDefaultLayouter() = true` only | Rejected — weak/no control of initial 
orientation; unlikely to fix bottom-up graphs |
+| Custom three-column `Layouter` (port Swing columns) | Rejected for this 
issue — higher maintenance; fights platform layout chrome; overkill for soft 
LTR preference |
+| Always force LTR and ignore toolbar orientation | Rejected — conflicts with 
soft-preference requirement |
+
+## Architecture
+
+Show Diagram already builds a toolkit-neutral snapshot and maps it to API 
nodes/edges. This change only supplies a custom layouter from extras.
+
+```
+StrutsDiagramProvider
+  └── StrutsDiagramExtras  (CommonDiagramExtras)
+        └── getCustomLayouter(settings, project)
+              ├── GraphManager.createHierarchicGroupLayouter()
+              ├── OrientationLayouter = LEFT_TO_RIGHT (always, Maven-like)
+              └── keep spacing/layerer tweaks minimal (only if needed for 
readability)
+```
+
+**Why not read `GraphSettings.getCurrentLayoutOrientation()` for the custom 
layouter?**  
+Platform default orientation is top-to-bottom. Honoring settings blindly would 
keep the broken bottom-up/top-to-bottom graphs. Soft preference is instead: if 
the user picks another toolbar layout algorithm (e.g. Organic or platform 
Hierarchic), Dom refresh must leave that choice alone; only our custom layouter 
path is LTR.
+
+**Unchanged:** `StrutsConfigDiagramModel`, `StrutsDiagramDataModel`, API 
node/edge adapters, Dom refresh, compact label chrome, Swing `fileEditor` / 
`ui`, `plugin.xml` registrations.
+
+## Components
+
+| Unit | Role |
+|---|---|
+| `StrutsDiagramExtras` | Override `getCustomLayouter`. Build a hierarchic 
group layouter and always set orientation to `LEFT_TO_RIGHT` (Maven/Gradle 
pattern). Leave `useDefaultLayouter()` at platform default (`false`). Keep 
`doEdgeLabeling()` default (`true`). Do not mutate `GraphSettings` current 
layouter on Dom refresh. |
+| `StrutsDiagramProvider` | No logic change; still returns the same extras 
instance. |
+| Snapshot / presentation | Untouched. |
+| Swing tab (`diagram.fileEditor` / `diagram.ui`) | Untouched. |
+
+No new production classes. No `plugin.xml` changes for this issue.
+
+Implementation should follow the public IntelliJ graph APIs used by 
Maven/Gradle extras (`GraphManager`, `HierarchicGroupLayouter`, 
`LayoutOrientation`), not raw yFiles types.
+
+## Data flow
+
+### Initial open
+
+1. User invokes Show Diagram on a Struts 2 config.
+2. Platform asks extras for a custom layouter (or resolves 
`DiagramLayout.CUSTOM` / matching custom class).
+3. Extras returns a hierarchic group layouter oriented `LEFT_TO_RIGHT`.
+4. Graph lays out with main flow left-to-right; edge labels remain enabled.
+
+### After user picks another layout algorithm
+
+1. User selects a non-custom layout from the diagram toolbar (e.g. Organic, 
platform Hierarchic).
+2. Platform stores that layouter on `GraphSettings`.
+3. Dom refresh / data reload must not call `setCurrentLayouter` back to our 
custom LTR layouter.
+4. `getCustomLayouter` may still return LTR hierarchic for the CUSTOM path, 
but the active toolbar choice remains whatever the user selected.
+
+## Error handling & edge cases
+
+| Scenario | Behavior |
+|---|---|
+| Default / first open / CUSTOM path | Hierarchic LTR |
+| User selects another layout algorithm | That algorithm stays active across 
Dom refresh |
+| User wants top-to-bottom | Choose platform Hierarchic (or equivalent) from 
toolbar — not our custom LTR path |
+| Multi-package / chain / redirect edges | Same model semantics; hierarchic 
LTR only changes placement |
+| Empty / unavailable model | Unchanged — no graph to lay out |
+| Edge label `success` / similar | Remains readable (`doEdgeLabeling` stays 
on) |
+| Swing Diagram tab | Unchanged |
+
+## Testing
+
+### Automated
+
+Extend `StrutsDiagramProviderTest` (or a focused extras test):
+
+1. Call `StrutsDiagramExtras.getCustomLayouter(...)` → assert a non-null 
hierarchic layouter oriented `LEFT_TO_RIGHT` (via the public orientation API 
available on 262).
+2. Optionally assert a second call still returns LTR (custom path is stable; 
soft preference is “don’t reset GraphSettings,” not “read orientation from 
settings”).
+3. Do not assert pixel coordinates or node bounding boxes.
+
+Gate: `./gradlew test -x rat --tests "com.intellij.struts2.diagram.*"`
+
+If constructing a real `GraphSettings` instance is awkward in light tests, use 
the smallest platform-supported fixture or stub that still exercises the real 
extras branch (prefer real graph settings APIs over brittle mocks).
+
+### Manual (`./gradlew runIde` on IU)
+
+1. Show Diagram on a small Struts config → package → action → result reads 
left-to-right.
+2. Confirm edge labels (e.g. `success`) remain readable.
+3. Pick a different layout algorithm from the toolbar, edit the XML so Dom 
refresh runs → the user’s layout choice remains (not forced back to custom LTR).
+
+### Out of scope
+
+- Pixel/layout assertions
+- Robot / UI e2e
+- Swing tab removal
+- Tests for #96–#100
+
+## Issue framing update
+
+When implementing, update 
[#122](https://github.com/apache/struts-intellij-plugin/issues/122) (comment or 
PR description) so acceptance criteria match this design:
+
+- Drop Swing tab LTR as the comparison baseline.
+- Keep the LTR hierarchical goal and model non-change constraint.
+- Add soft-preference acceptance: a user-selected non-custom layout algorithm 
is not reset on Dom refresh.
+
+## Future work (out of scope)
+
+- 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 [#122](https://github.com/apache/struts-intellij-plugin/issues/122)
+- `com.intellij.diagram.extras.DiagramExtras#getCustomLayouter`
+- `com.intellij.diagram.extras.DiagramExtras#useDefaultLayouter`
+- `com.intellij.openapi.graph.layout.LayoutOrientation`
+- `com.intellij.openapi.graph.GraphManager#createHierarchicGroupLayouter`
+- Maven/Gradle UML extras `getCustomLayouter` (platform reference pattern)
+- `com.intellij.struts2.diagram.provider.StrutsDiagramExtras`
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 512bca0..0f4292d 100644
--- 
a/src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java
+++ 
b/src/main/java/com/intellij/struts2/diagram/provider/StrutsDiagramExtras.java
@@ -24,7 +24,13 @@ import 
com.intellij.diagram.extras.custom.CommonDiagramExtras;
 import com.intellij.openapi.actionSystem.CommonDataKeys;
 import com.intellij.openapi.actionSystem.DataSink;
 import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.graph.GraphManager;
+import com.intellij.openapi.graph.layout.Layouter;
+import com.intellij.openapi.graph.layout.LayoutOrientation;
+import com.intellij.openapi.graph.layout.hierarchic.HierarchicGroupLayouter;
+import com.intellij.openapi.graph.settings.GraphSettings;
 import com.intellij.openapi.graph.view.NodeRealizer;
+import com.intellij.openapi.project.Project;
 import com.intellij.pom.Navigatable;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.SmartPsiElementPointer;
@@ -60,6 +66,22 @@ public final class StrutsDiagramExtras extends 
CommonDiagramExtras<StrutsDiagram
         return false;
     }
 
+    /**
+     * 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}.
+     */
+    @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));
+        return layouter;
+    }
+
     @Override
     public @NotNull JComponent createNodeComponent(@NotNull 
DiagramNode<StrutsDiagramItem> node,
                                                    @NotNull DiagramBuilder 
builder,
diff --git 
a/src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java 
b/src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
index 9d11f16..324297d 100644
--- a/src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
+++ b/src/test/java/com/intellij/struts2/diagram/StrutsDiagramProviderTest.java
@@ -29,6 +29,11 @@ import com.intellij.openapi.actionSystem.DataContext;
 import com.intellij.openapi.actionSystem.impl.SimpleDataContext;
 import com.intellij.openapi.application.ReadAction;
 import com.intellij.openapi.editor.colors.EditorColorsManager;
+import com.intellij.openapi.graph.layout.CanonicMultiStageLayouter;
+import com.intellij.openapi.graph.layout.Layouter;
+import com.intellij.openapi.graph.layout.LayoutOrientation;
+import com.intellij.openapi.graph.layout.hierarchic.HierarchicGroupLayouter;
+import com.intellij.openapi.graph.settings.GraphSettings;
 import com.intellij.openapi.graph.view.Graph2D;
 import com.intellij.openapi.graph.view.NodeRealizer;
 import com.intellij.openapi.vfs.VirtualFile;
@@ -201,6 +206,29 @@ public class StrutsDiagramProviderTest extends 
BasicLightHighlightingTestCase {
                 getProvider().getExtras().isZoomAnimationsEnabled());
     }
 
+    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);
+
+        CanonicMultiStageLayouter multiStage = (CanonicMultiStageLayouter) 
layouter;
+        assertEquals("Custom layouter must be left-to-right (Maven/Gradle 
pattern)",
+                LayoutOrientation.LEFT_TO_RIGHT,
+                multiStage.getLayoutOrientation());
+
+        // 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());
+    }
+
     /**
      * Minimal DiagramBuilder for createLabelNode / setNodeBorders.
      * No Mockito in this project — use JDK proxies.

Reply via email to