agents setup

This commit is contained in:
Bane Stojkovic
2026-09-04 15:31:00 +02:00
parent 26d6ca48fd
commit 6f51c3ca42
7 changed files with 598 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
---
name: add-plugin
description: Add or modify a DIYLC Swing plugin, menu action, toolbar item, dialog or side panel. Use when asked to add a UI feature, a menu entry, a new tool window, a background task, or to react to domain events like selection or project changes.
---
# Adding a plugin or UI feature to DIYLC
The Swing module is a shell that hosts plugins. Everything the user sees — the toolbox, the canvas,
every menu, the status bar, autosave — is an `IPlugIn`. Adding UI means adding or extending a plugin,
not reaching into frames.
## Where things go
| Path | Contents |
| --- | --- |
| `diylc-swing/src/main/java/org/diylc/swing/plugins/<name>/` | One package per plugin |
| `diylc-swing/src/main/java/org/diylc/swing/actions/` | `AbstractAction` implementations, grouped into `file`, `edit`, `analyze` sub-packages |
| `diylc-swing/src/main/java/org/diylc/swing/gui/` | `MainFrame`, dialogs, editors, shared components |
| `diylc-swing/src/main/java/org/diylc/swing/gui/MainFrame.java` | Where plugins are installed |
## Before writing anything
Read the plugin closest to what is being asked. `plugins/autosave/AutoSavePlugin.java` is the
smallest complete example (event subscription plus background work);
`plugins/statusbar/`, `plugins/layers/` and `plugins/analyze/` show menu and panel injection.
Match the one you read.
## The plugin
```java
/* ... GPL header from HEADER.txt ... */
package org.diylc.swing.plugins.myfeature;
public class MyFeaturePlugin implements IPlugIn {
private static final Logger LOG = Logger.getLogger(MyFeaturePlugin.class);
private ISwingUI swingUI;
private IPlugInPort plugInPort;
public MyFeaturePlugin(ISwingUI swingUI) {
this.swingUI = swingUI;
}
@Override
public void connect(IPlugInPort plugInPort) {
this.plugInPort = plugInPort;
swingUI.injectMenuAction(new MyAction(plugInPort), "Tools");
}
@Override
public EnumSet<EventType> getSubscribedEventTypes() {
return EnumSet.of(EventType.SELECTION_CHANGED);
}
@Override
public void processMessage(EventType eventType, Object... params) {
if (eventType == EventType.SELECTION_CHANGED) {
...
}
}
}
```
Install it in `MainFrame` alongside the others:
```java
presenter.installPlugin(() -> new MyFeaturePlugin(this));
```
Order matters for menus and for docked panels — insert the line where the resulting menu or panel
position is correct, not blindly at the end.
## Talking to the domain
Plugins reach the domain **only** through `IPlugInPort`. Never import a concrete component type into
a plugin unless the feature is genuinely component-specific, and never call into `Presenter`
directly. If `IPlugInPort` lacks what you need, adding a method there is the right move — but it also
means touching `diylc-core`, so keep the addition minimal and in the style of its neighbours.
## Reacting to events
`getSubscribedEventTypes()` returns an `EnumSet<EventType>`. Subscribe to the narrowest set that
works. `PROJECT_MODIFIED`, `REPAINT` and `MOUSE_MOVED` fire constantly — anything non-trivial in
response to those will be felt as lag. The available types are in
`diylc-core/src/main/java/org/diylc/common/EventType.java` (`PROJECT_LOADED`, `PROJECT_SAVED`,
`SELECTION_CHANGED`, `ZOOM_CHANGED`, `SLOT_CHANGED`, `LAYER_STATE_CHANGED`, `CLOUD_LOGGED_IN`, …).
## Injecting UI
- `swingUI.injectMenuAction(action, "Menu Name")` — a menu item; passing a `null` action inserts a
separator, and a missing menu is created automatically.
- `swingUI.injectSubmenu(name, icon, parentMenuName)` and
`injectDynamicSubmenu(name, icon, parentMenuName, handler)` for submenus.
- `swingUI.injectGUIComponent(component, SwingConstants.LEFT, collapsible, visibilityConfigKey)` for
a docked panel. `TOP`, `BOTTOM`, `LEFT`, `RIGHT` are valid; `CENTER` is reserved for the canvas.
Pass a config key for collapsible panels so the open/closed state persists.
- `swingUI.getOwnerFrame()` is the parent for dialogs.
## Actions
Actions are small classes extending `javax.swing.AbstractAction`, holding an `IPlugInPort`, setting
their label with `putValue(AbstractAction.NAME, ...)`, and delegating to `plugInPort` in
`actionPerformed`. See `actions/RenumberAction.java` for the minimal shape. They carry
`private static final long serialVersionUID = 1L;` like any Swing class.
## Background work
Never block the EDT. Use `swingUI.executeBackgroundTask(task, blockUI)` with an `ITask<T>`, which
handles the busy cursor and optional glass pane, or a dedicated executor for periodic work as
`AutoSavePlugin` does.
## Settings and strings
- Persisted preferences: `ConfigurationManager.getInstance().readString(KEY, default)` and
`writeValue(KEY, value)`, with the key as a `String` constant. Implement `IConfigListener` to react
to changes.
- Every user-visible string goes through `LangUtil.translate("...")`.
- Icons come from `IconLoader`.
## Verify
```bash
mvn -q -pl diylc-swing -am compile
```
Compilation is not evidence that the UI works. Say so, and offer to walk through launching the app
via `.run/DIYLCStarter.run.xml` (which carries the required `--add-opens` / `--add-exports` flags) for
a visual check.