globaltcad/swing-tree/agent-skills

Claude Code and agent skill by globaltcad/swing-tree

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Write Swing desktop UIs with the SwingTree library and its companion property library Sprouts. Use this skill whenever you are building, editing, reviewing, or debugging Java Swing GUI code that imports `swingtree.UI` or `sprouts.*` — declarative component trees, convergent/responsive/reactive layouts, the functional style API, SVG icons, animations, and MVI/MVL or MVVM view models.

Install

npx vskill@latest install globaltcad/swing-tree/agent-skills

SKILL.md

Writing SwingTree Applications

SwingTree is a Java library for building Swing desktop GUIs declaratively, the way Flutter / SwiftUI / Jetpack Compose / JetBrains' Kotlin UI DSL build theirs. You describe the component tree with method chaining + nesting, bind it to state with the Sprouts property library (Var/Val/Vars/Tuple), and paint it with a functional, immutable style API. There is no XML, no FXML, no separate template language — it is all plain Java, fully type-safe and debuggable.

This document gives you the intuition to write any SwingTree app: the builder, layout, properties & lenses, the two architecture patterns (MVI/MVL and classic MVVM), events, styling, animation, tables, icons & SVG, dialogs, and the non-obvious gotchas that bite people. Read it top to bottom once; thereafter use the cheat sheet at the end.

**One library-wide preference up front: SwingTree views are expected to be
*convergent*** — usable whether the window is maximised on an ultrawide or
tiled into a tall, 500-pixel strip. Users run tiling window managers, snap
windows to halves, and rotate monitors into portrait; a view that only works
at the size you developed it in is considered broken. §2c is the short
version and the checklist; apply it to every view you write or review.

0. The one import and the mental model

java
import swingtree.UI;
import static swingtree.UI.*; // brings panel(), button(), FILL, WRAP, GROW, ...

A UI is a tree of components. Every node is built by a UI.xxx(..) factory that returns a builder (UIForPanel, UIForButton, UIForLabel, … all subtypes of UIForAnySwing). On a builder you:

  • configure it with chained withXyz(..) / isXyzIf(..) calls,
  • nest children with .add(..),
  • bind it to Var/Val properties for reactivity,
  • style it with .withStyle(it -> ...),
  • wire events with .onXyz(..),
  • and finally unwrap it with .get(JPanel.class) or hand it to UI.show(..).

Crucial idea: a builder is a recipe, not the component. It produces a real JComponent underneath. You can escape to the raw component with .peek(c -> ...) or unwrap with .get(Type.class) — but treat peek as a last resort (§12): reach for a SwingTree with*/is*If/on* method first.

The smallest complete program:

java
import static swingtree.UI.*;
public static void main(String[] args) {
UI.show(
panel("wrap 1")
.add(label("Welcome to SwingTree!"))
.add(button("Click me").onClick(it -> System.out.println("clicked")))
);
}

1. Growing the tree — factories, nesting, add

UI.show(component | builder | title, builder | Function<JFrame,Component>) opens a window. Inside it you compose nodes:

java
UI.show(
panel("wrap 2") // a JPanel, MigLayout "wrap 2"
.add(label("Name:"))
.add("grow", textField("John")) // first String arg = per-child layout constraint
.add(label("Age:"))
.add("grow", textField("42"))
.add("span", separator()) // span all columns, then wrap
.add(button("Save"))
);

Rules of .add(..):

  • .add(childBuilder) — add with no constraint.
  • .add("growx, span 2", childBuilder) — first arg is a MigLayout add-constraint string.
  • .add(GROW.and(SPAN), childBuilder) — or a type-safe constraint (see §2).
  • .add(a, b, c) — add several children at once (same constraint applies to each).
Common factories (each returns a builder)
FactoryComponent
panel(...), box(...)JPanel / JBox (a transparent, insets-free panel — perfect for grouping)
label(text), html("<h1>..</h1>")JLabel (html(..) renders HTML)
button(text), toggleButton(text), checkBox(text), radioButton(text)buttons
textField(text), textArea(text), passwordField(), numericTextField(var)text inputs
comboBox(...), slider(Axis, min, max), spinner(...), progressBar(...)value pickers
separator(), scrollPane(), scrollPanels(), splitPane(Axis), tabbedPane()structure
table(Var<TableData>), table(), tree(Var<N>, conf), trees(Var<Tuple<N>>, conf), list(...), menu(...), menuItem(...), splitButton(text)data / menus (bind a TableData value — see §10; a tree binds one nested value, a forest a tuple of them — see §10)
icon(path), icon(w,h,path)JIcon (supports SVG, see §10)

box(...) vs panel(...): a JBox is non-opaque with zero default insets — use it for invisible structural grouping; use panel when you want a real surface to style. Never call `setOpaque(..)` yourself on a styled component — the style engine owns opacity and will fight you.

Wrapping a custom / third-party component
java
.add( UI.of(new MyCustomJComponent()).onMouseClick(it -> ...) )

UI.of(jcomponent) wraps any JComponent so the declaration keeps flowing. UI.of(this) is the standard way to start a View extends JPanel (see §5).


2. Layout — MigLayout, type-safe constants, and convergence

SwingTree's default layout manager is MigLayout; §2a and §2b are the two ways to drive it. §2c onwards is about making the result work at any window size, which SwingTree treats as the default expectation rather than a nice-to-have.

2a. String constraints (most common, terse)

The container constraint goes in the factory; per-child constraints go as the first add(..) arg:

java
panel("fill, wrap 3, insets 12, gap 8") // container: fill space, 3 cols, 12px insets
.add("growx", a)
.add("span 2, growx", b) // this child spans 2 columns
.add("wrap", c) // force a new row after c

Memorize these MigLayout keywords:

  • Container: fill, fillx, filly, wrap N (N columns), insets T L B R / ins N, gap, debug (draws guide borders — great for diagnosing layout).
  • Per-child: grow, growx, growy, push, pushx, pushy, span / span N, wrap, align center/left/right, top/bottom, width 60px::, w 180!, h 90!.
  • 60px:: means "min 60, no max"; 180! means "exactly 180".

withLayout("fill, wrap 2") sets the container constraint after the fact, and withLayout(layout, colConstraints, rowConstraints) gives full control, e.g. .withLayout("fill, wrap 2", "[grow 60][grow 40]").

Full keyword reference: http://www.miglayout.com/

2b. Type-safe constants (refactor-safe, composable)

import static swingtree.UI.* exposes constants that compose with .and(..):

java
of(this).withLayout(FILL.and(WRAP(1)).and(INS(16)))
.add(GROW.and(PUSH), child)
.add(CENTER.and(SPAN), html("<h2>Title</h2>"))
.add(RIGHT, button("OK"));

Container constants: FILL, FILL_X, FILL_Y, WRAP(n), INS(n) / INS(t,l,b,r), GAP_REL(n), FLOW_X, DEBUG. Per-child constants: GROW, GROW_X, GROW_Y, PUSH, PUSH_X, PUSH_Y, SPAN, SPAN(n), WRAP, SHRINK, CENTER, LEFT, RIGHT, TOP, BOTTOM, ALIGN_CENTER, ALIGN_LEFT, ALIGN_X_CENTER, ALIGN_Y_TOP, GAP_LEFT(n), …

String constraints and constants are interchangeable — pick whichever reads clearer locally. (Examples in this codebase mix both freely.)

The geometry enums (UI.Axis, UI.Placement, and friends)

Wherever Swing wants an int constant, SwingTree wants one of these. They are the vocabulary the whole API shares, so learn them once:

  • `UI.Axis` — HORIZONTAL, VERTICAL, LINE, PAGE. Which way a thing

runs: slider(Axis), progressBar(Axis, ..), separator(Axis), splitPane(Axis), toolBar(Axis), scrollPanels(Axis, ..), Layout.box(Axis), withBoxLayout(Axis). LINE and PAGE follow the container's ComponentOrientation, but only a BoxLayout acts on that difference; every other component calls Axis.resolve() and so reads LINE as HORIZONTAL and PAGE as VERTICAL.

  • `UI.Placement` — UNDEFINED, the four corners (TOP_LEFT …), the middle

of the four sides (TOP, LEFT, BOTTOM, RIGHT) and CENTER. One named point of a rectangle: img.placement(..), text.placement(..), SvgIcon.withPreferredPlacement(..), label(text, Placement), withAlignment(..), withTextPosition(..), fontAlignment(..). Careful: Placement.TOP is the middle of the top edge, so aligning a label with it also centres the label horizontally. Use Placement.TOP_LEFT to pin both axes.

  • `UI.Side` (TOP/LEFT/BOTTOM/RIGHT) picks an edge to dock to, e.g.

tabbedPane(Side.LEFT). `UI.Edge` picks an edge to draw on (borderAt(Edge.TOP, 1, color)), `UI.Corner` picks a corner to round (borderRadiusAt(Corner.TOP_LEFT, w, h), plus Corner.EVERY).

  • `UI.Span` names the direction a gradient travels — either side to side

(LEFT_TO_RIGHT, TOP_TO_BOTTOM, …) or corner to corner (TOP_LEFT_TO_BOTTOM_RIGHT, …).

  • `UI.ComponentArea` (ALL, EXTERIOR, BORDER, INTERIOR, BODY) names

a region of the box model and `UI.ComponentBoundary` (OUTER_TO_EXTERIOR … CENTER_TO_CONTENT) names the line between two regions. clipTo(..) and parentFilter(f -> f.area(..)) take an area; gradient(g -> g.boundary(..)) takes a boundary.

Each of these carries small helper methods so you rarely have to write a switch over them yourself — Axis.perpendicular(), Side.opposite(), Placement.opposite(), Span.reversed(), Cursor.resizeAt(Side), Active.decide(boolean), FontStyle.withBold(boolean).

2c. Convergence — a SwingTree view is expected to survive any window shape

This is a strong preference of the library, not an optional polish step. Write convergent views by default; treat "it only works maximised on a landscape monitor" as a bug.

Desktop windows are not a fixed size any more. Users run tiling window managers (i3, sway, Hyprland, yabai, AeroSpace, FancyZones), snap windows to halves and thirds, put four windows across an ultrawide, rotate a monitor into portrait, and drag your app onto a smaller second screen. A view that assumes ~1400×900 is broken for a large fraction of real users.

Convergent ≠ merely responsive. Responsive means nothing overlaps; convergent means nothing is lost — the layout rearranges, the content re-prioritises, and the primary action stays reachable at every size.

The default recipe (start every page like this)
java
UI.scrollPane( conf -> conf.fitWidth(true) ) // the page may outgrow the window
.withHorizontalScrollBarPolicy(UI.Active.NEVER) // never scroll sideways
.withVerticalScrollIncrement(24)
.add(
UI.panel().withFlowLayout(UI.HorizontalAlignment.LEFT, 18, 18)
.withMinSize(0, 0) // may shrink to whatever it is given
.withPrefSize(PAGE_REFERENCE_WIDTH, 0) // the reference width (see 2d)
.add(SIDEBAR_SPAN, sidebar(vm))
.add(CONTENT_SPAN, content(vm))
);
Four mechanisms, in the order you should reach for them
GearMechanismUse whenCosts
0"wmin 0" + withMinSize(0,0)always — a prerequisite for all of the othersnothing
1withFlowLayout() + AUTO_SPAN (§2d)the same regions want a different number of columnsno state at all
2Var<Layout> reflow (§2e)the same widgets want a genuinely different arrangement (a toolbar folding into rows)one property; nothing is rebuilt
3form-factor state + view swap (§2f)the two shapes want different component trees (split pane ⇄ scrolling column)rebuilds — loses focus/caret/scroll
4isVisibleIf + shorter bound labels (below)content, not layout, must re-prioritisenothing

Most views need gear 0 + gear 1. Escalate only when the shape of the problem demands it — gear 3 is the only one that destroys component state.

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
55
Repository forks
4
7-day trend score
20

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/globaltcad/swing-tree/agent-skills/badge)](https://verified-skill.com/skills/globaltcad/swing-tree/agent-skills)

Something wrong with this skill or its page? Tell us.