Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
1a24e1e
feat: stub initial new UI for Alchemist
nicolasfara Apr 17, 2026
e55592e
feat: start integrating the simulation commands in UI
nicolasfara Apr 17, 2026
beb19a5
feat: add link connection and fix start/stop button
nicolasfara Apr 17, 2026
d61ee47
refactor: revise a bit the layout
nicolasfara Apr 17, 2026
e718d8a
refactor: minor layout improvements
nicolasfara Apr 17, 2026
1c787f3
refactor: code refactor in UI compose
nicolasfara Apr 18, 2026
656347b
perf: optimize rendering and simulation bindings
nicolasfara Apr 18, 2026
65e53ee
feat: use light theme
nicolasfara Apr 18, 2026
5b910bd
feat: stat simulator with 3/4 measure size
nicolasfara Apr 18, 2026
fabc634
feat: umprove UI
nicolasfara Apr 20, 2026
5b4a994
feat: simplify grid dimension layout
nicolasfara Apr 20, 2026
9c09f12
feat: new dock bar
nicolasfara Apr 21, 2026
5044305
refactor: improve ui
nicolasfara Apr 21, 2026
e8acc1b
feat: implement right-click pan and fixed problem with opening right …
nicolasfara Apr 22, 2026
d0e2511
feat: implement group selection
nicolasfara Apr 22, 2026
66cf1ab
refactor: move UI code into appropriate packages and refactor throttling
nicolasfara Apr 22, 2026
af36dd0
feat: implement drag-and-drop feature for the nodes
nicolasfara Apr 23, 2026
a828d1e
fix: minor fix on rendering updated positions
nicolasfara Apr 23, 2026
f2a9f9e
perf: some optimizations
nicolasfara Apr 24, 2026
bdb89ac
refactor: improve code
nicolasfara Apr 27, 2026
0f9c8bf
perf: optimize data structures with immutable ones
nicolasfara May 22, 2026
e57282f
chore: improve accessibility
nicolasfara May 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Alchemist Compose UI

## Goals
- Provide a modern Compose-based simulator UI centered on the rendered environment.
- Keep the visual structure common across targets, with platform modules only adapting simulator state and commands.
- Establish a living specification that can grow with future iterations without forcing a redesign of the document.

## Design Principles
- Canvas first: the simulation viewport is the dominant surface and receives the strongest visual emphasis.
- Common by default: layout, presentation logic, theming, and interaction shell live in `commonMain`.
- Thin platform adapters: JVM, JS, and WASM modules only translate simulator state into common UI models and dispatch user actions back to the host runtime.
- Progressive disclosure: node details stay out of the main viewport until a selection occurs.
- Low-chrome modernity: restrained translucent panels, clear hierarchy, and motion only where it explains state change.

## Information Architecture
### Primary regions
- Central viewport: renders nodes and, when available, map-oriented backdrops.
- Bottom control dock: hosts transport controls, status, time, step, and progress.
- Contextual inspector: reveals information for the selected node.

### Secondary overlays
- Viewport summary chips: node count, dimensions, and scene backdrop.
- Empty/loading/error copy: rendered inside the viewport shell without changing the platform adapter contract.
- Compact inspector shell: becomes a drawer or bottom sheet on narrow layouts.

## Layouts
### Wide layout
- Viewport fills the remaining space.
- Bottom control dock is anchored to the bottom center.
- Inspector is anchored to the right edge and remains visible while a node is selected.

### Compact layout
- Viewport stays full-width above the bottom dock.
- Inspector becomes an overlay sheet above the controls.
- The scrim dismisses the inspector when the user clicks outside it.

## Components
### Viewport
- Compose canvas with a shared visual shell and background treatment.
- Supports node highlighting, node hit-testing, drag-to-pan, mouse-wheel zoom, and summary overlays.
- Accepts normalized viewport nodes from the platform adapter rather than raw simulator entities.

### Control dock
- Play, pause, and step actions.
- Status pill with color-coded simulator state.
- Time and step metric blocks.
- Progress bar with determinate mode when completion is known, indeterminate otherwise.

### Node inspector
- Header with node identifier and dismiss action.
- Position section.
- Concentrations section.
- Metadata section for simulator-provided details already available or straightforward to expose.

## State Model
- `AlchemistUiState`: top-level UI state for the screen.
- `ViewportScene`: viewport payload including nodes, dimensions, backdrop, summary chips, and viewport copy.
- `SimulationControlsState`: simulator status, time label, step count, and progress descriptor.
- `NodeInspectorState`: visible node details for the selected node.
- `ComposeUiStateStore`: thread-safe holder updated by platform monitors/adapters.

## Platform Integration Boundaries
- `commonMain` owns:
- theming
- layout
- viewport rendering shell
- node selection interactions
- control dock presentation
- inspector presentation
- `jvmMain`, `jsMain`, and `wasmJsMain` own:
- lifecycle bootstrapping
- mapping simulator/runtime state into common UI models
- dispatching user actions such as play, pause, and step
- platform-specific transport or monitor glue

## Interaction Patterns
- Clicking a node opens the inspector for that node.
- Clicking outside a selected node dismisses the inspector.
- Holding the middle mouse button and dragging pans the camera.
- Using the mouse wheel zooms the viewport in and out around the pointer position.
- Panning is unbounded in every direction, with no camera clamp (CAD-like navigation).
- Control buttons reflect simulator availability:
- `Play` is enabled in ready/paused states.
- `Pause` is enabled while running.
- `Step` is enabled in ready/paused states.
- The step action is part of the UI contract even if some targets still need dedicated wiring.

## Visual Tokens
- Palette: deep blue/slate surfaces with warm amber and cool cyan accents.
- Shapes: large rounded panels for the dock, viewport shell, and inspector.
- Typography:
- serif headings for emphasis
- monospace metrics for time, steps, and machine-like values
- Motion:
- inspector slide/fade transitions
- no decorative motion in the viewport beyond state-relevant highlighting

## Accessibility
- Controls use text labels instead of icon-only affordances.
- Status is encoded with both text and color.
- Important values remain visible in compact mode.
- Inspector dismissal is possible both from an explicit button and by clicking the compact-mode scrim.

## Open Questions / Future Iterations
- Dedicated map tile or raster/vector map underlay support.
- Fit-to-scene and reset-camera shortcuts.
- Additional inspector sections for reactions, neighborhood members, and domain-specific node properties.
- Multi-selection and aggregate inspector views.
- Richer progress semantics for simulations that expose completion estimates.
267 changes: 267 additions & 0 deletions alchemist-composeui/AI_CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
# `alchemist-composeui` — AI Maintenance Guide

## Intent

`alchemist-composeui` provides the Compose-based user interface for Alchemist. The module exists to help users and agentic maintainers:

- inspect a running simulation visually,
- control playback with play/pause/step, jump, FPS, and pacing actions,
- inspect nodes in detail,
- reposition selected nodes with `Ctrl + left drag`,
- toggle link visibility,
- run the same UI shell across JVM desktop and browser targets.

This document is intentionally **intent-driven**: it describes why the module exists, what users expect from it, and what invariants future changes must preserve.

## Primary jobs to be done

1. Render a live simulation viewport with nodes and optional edges.
2. Expose transport controls plus direct jump and pacing controls.
3. Show a node inspector when a node is selected.
4. Move selected nodes from the viewport and commit the new positions to the simulator.
5. Keep the UI state reactive and thread-safe.
6. Bridge the simulation engine to the Compose UI on JVM.
7. Provide a demo/fallback shell for non-simulation entrypoints.

## Current architecture

### Module structure

- `src/commonMain`: shared UI, state, and rendering logic.
- `src/jvmMain`: desktop monitor, simulation bridge, JVM-specific platform entrypoint.
- `src/wasmJsMain`: browser entrypoint and platform glue.

### Runtime entrypoints

- `App.kt` renders the shared UI shell.
- `AlchemistUiRoot.kt` composes the page layout and chooses compact vs wide layout.
- `ComposeMonitor.kt` integrates the UI with Alchemist's `OutputMonitor` on JVM.
- `DesktopAlchemistUiCallback.kt` forwards UI actions to the running simulation.
- `Main.kt` under `wasmJsMain` launches the browser demo entrypoint.

### State model

Defined in `UiModel.kt`:

- `AlchemistUiState` is the top-level UI snapshot.
- `ViewportScene` models the center canvas.
- `SimulationControlsState` models play/pause/step plus:
- current time and step labels,
- `To Time` and `To Step` text input values,
- editable UI FPS,
- event-rate slider state,
- modal validation errors.
- `NodeInspectorState` models the selected node panel.
- `AlchemistUiCallbacks` is the interaction contract.
- `NoOpUiCallbacks` is the fallback implementation.

### State storage

Defined in `UiStore.kt`:

- `ComposeUiStateStore` wraps a `MutableStateFlow`.
- `ComposeUiController` bundles the store and callbacks.
- `demoController()` provides the browser/demo state and behavior.

### Rendering components

Shared UI is decomposed into focused composables:

- `ViewportSurface.kt`: central interactive canvas, pan/zoom, hit detection.
- `SimulationPrimaryPane.kt`: viewport + bottom control dock.
- `ControlDock.kt`: transport controls, metrics, jump inputs, FPS input, and event-rate slider.
- `NodeInspector.kt`: selected node details panel.
- `SummaryRail.kt`: summary chips and link toggle.
- `InspectorSection.kt`, `MetricBlock.kt`, `StatusPill.kt`, `TransportButton.kt`, `ProgressSection.kt`: small UI primitives.
- `Theme.kt`: colors and visual constants.
- `ViewportProjection.kt` and `ViewportRendering.kt`: coordinate transforms and drawing helpers.

### JVM data bridge

Defined in `adapter/AlchemistNodeAdapter.kt` and used by `ComposeMonitor.kt`:

- simulation nodes are projected into `ViewportNode` objects,
- environment edges are converted into `ViewportEdge`,
- simulation status is mapped into `SimulationStatus`.

## User-facing behavior

### What the user should see

- A simulation window titled `Alchemist` on desktop.
- A viewport centered on the simulation data.
- Nodes rendered with stable positions and accent-based coloring.
- Optional link rendering controlled by the UI.
- A bottom transport dock with:
- Play,
- Pause,
- Step,
- time label,
- step counter,
- `To Time` text box,
- `To Step` text box,
- `FPS` text box,
- events/second slider ending in `Max` for full throttle.
- A node inspector when a node is selected.
- A modal popup when a jump target or numeric input is invalid.

### Interaction model

- Left-click a node to inspect it.
- Click empty space to dismiss the inspector.
- Drag a selection box with left-drag.
- Hold `Ctrl` and left-drag from a selected node to translate the full selection while preserving inter-node distances.
- `Ctrl + left drag` commits the new node positions to the simulator only when the pointer is released.
- If `Ctrl + left drag` starts on empty space or an unselected node, keep the normal click/selection behavior.
- Middle-drag to pan the viewport.
- Wheel to zoom.
- Toggle links from the summary rail.
- Use play/pause/step to control the simulation when the UI is attached to a live engine.
- Press Enter in `To Time` / `To Step` to fast-forward to the requested target.
- `To Time` / `To Step` reject backward targets.
- If the simulation was already running, jump actions resume it immediately after the target is reached.
- Use `FPS` to control UI refresh frequency, clamped between `5` and the detected monitor refresh rate, or `60` if detection is unavailable.
- Use the event-rate slider to pace the simulation thread; the terminal `Max` value disables pacing and runs full throttle.

## Important invariants

1. **UI state must remain single-sourced**
- The view layer observes `ComposeUiStateStore.stateFlow`.
- Updates should go through the store, not through ad-hoc mutable globals.

2. **Callbacks are the only interaction boundary**
- Composables should call `AlchemistUiCallbacks` and stay agnostic of the simulation backend.

3. **Viewport projection must remain stable enough for inspection**
- `ViewportProjection` fixes the mapping once a valid viewport exists.
- The selection, drag translation, and zoom/pan logic assume a consistent mapping between world and screen space.

4. **Live monitor updates must preserve UI-only toggles**
- `ComposeMonitor.updateUiState` intentionally preserves `scene.showLinks` while refreshing the scene from the simulation.

5. **Validation feedback must remain store-driven**
- Invalid jump/FPS input is surfaced via `SimulationControlsState.dialog`.
- Composables only render and dismiss the dialog through callbacks.

6. **Demo state should remain usable without a live simulation**
- `demoController()` is the browser-friendly fallback and should continue to demonstrate the UI shell.

7. **Node dragging is preview-first and simulator-authoritative**
- The viewport may preview translated node positions locally during `Ctrl + drag`.
- JVM simulator state must be mutated only on drag release, then the UI must resync from the environment snapshot.
- Only X/Y are translated from the viewport gesture; any higher coordinates must remain unchanged.

7. **Small composables should stay small**
- Maintain the current composition pattern: one responsibility per file when possible.

## Known implementation details

### `ComposeMonitor`

- Starts a desktop Compose window lazily and only once.
- Throttles UI updates according to the current FPS stored in `SimulationControlsState`.
- Detects monitor refresh rate on JVM and falls back to `60` when unavailable.
- Paces the simulation thread according to the selected events/second slider value.
- Uses `Toolkit.getDefaultToolkit().screenSize` to size the window.
- Bridges simulation state into the UI state store.

### `DesktopAlchemistUiCallback`

- Executes simulation actions on JVM.
- Synchronizes store updates on `Dispatchers.Main.immediate`.
- Uses the simulation object as the source of truth for play/pause/step/jump state.
- Validates `To Time`, `To Step`, and `FPS` submissions before mutating simulator state.
- Restores running state after a successful jump when the simulation was already running.
- Commits dragged-node positions through `simulation.schedule { ... }`, `environment.moveNodeToPosition(...)`, and `simulation.nodeMoved(...)`.

### `ViewportSurface`

- Manages viewport size, camera pan, and zoom.
- Uses projection data derived from the current scene.
- Keeps the first valid projection fixed until the layout becomes valid.
- Requires node selection hit tests to respect camera zoom.
- Supports selection-box drag and `Ctrl + left drag` translation for already selected nodes.
- Must preserve existing selection behavior when a `Ctrl` gesture does not start on a selected node.

### `demoController()`

- Builds a sample scene with nodes, edges, summary data, and a mock progress state.
- Supports play/pause/step and link toggling without a live simulation.
- Mirrors the running-state jump behavior used on JVM.

## Requirements for future changes

When changing this module, preserve the following:

- Keep `commonMain` free of JVM-only dependencies.
- Keep the UI reactive through `StateFlow`.
- Preserve browser entrypoint usability.
- Preserve the JVM monitor contract with `OutputMonitor`.
- Preserve node inspection, transport controls, and link toggling.
- Preserve selection-box, pan, and zoom interactions while adding node dragging.
- Preserve jump validation semantics: backward `To Time` / `To Step` targets must fail with a popup.
- Preserve the constants governing FPS and event-rate ranges.
- Add or update tests when changing projection math, selection logic, node dragging, or state transitions.

## Preferred change strategy

1. Identify the user intent first.
2. Locate the smallest composable or state object that owns that behavior.
3. Keep shared UI logic in `commonMain`.
4. Add platform-specific code only in the relevant source set.
5. Verify that the demo shell still works after the change.
6. Verify that the JVM monitor still attaches to the simulation without breaking state updates.

## Test and verification checklist

Before considering a change complete, check:

- `ViewportProjectionTest` still passes.
- `SimulationControlsState` behavior still matches the expected play/pause/step rules.
- Jump/FPS validation still matches the expected dialog behavior.
- Adapter tests still cover canonical edge and viewport conversion behavior.
- The UI compiles in both `commonMain` and `jvmMain`.
- The desktop monitor still opens a window and updates the UI state.
- The browser entrypoint still renders the shared `app()` shell.

## What not to change casually

- The top-level `AlchemistUiState` shape.
- The callback contract in `AlchemistUiCallbacks`.
- The preservation of `scene.showLinks` across live updates.
- The viewport math without corresponding tests.
- The separation between shared UI and platform entrypoints.

## Suggested maintainer workflow

For future modifications, prefer this order:

1. Read this file.
2. Inspect `UiModel.kt` and the relevant composable.
3. Check platform-specific behavior in `ComposeMonitor.kt` or `Main.kt` if needed.
4. Update tests near the affected logic.
5. Re-run the module validation tasks.

## Quick map of key files

- `src/commonMain/kotlin/it/unibo/alchemist/boundary/composeui/UiModel.kt`
- `src/commonMain/kotlin/it/unibo/alchemist/boundary/composeui/UiStore.kt`
- `src/commonMain/kotlin/it/unibo/alchemist/boundary/composeui/AlchemistUiRoot.kt`
- `src/commonMain/kotlin/it/unibo/alchemist/boundary/composeui/ViewportSurface.kt`
- `src/commonMain/kotlin/it/unibo/alchemist/boundary/composeui/ControlDock.kt`
- `src/commonMain/kotlin/it/unibo/alchemist/boundary/composeui/NodeInspector.kt`
- `src/jvmMain/kotlin/it/unibo/alchemist/boundary/composeui/ComposeMonitor.kt`
- `src/jvmMain/kotlin/it/unibo/alchemist/boundary/composeui/DesktopAlchemistUiCallback.kt`
- `src/jvmMain/kotlin/it/unibo/alchemist/boundary/composeui/adapter/AlchemistNodeAdapter.kt`
- `src/wasmJsMain/kotlin/it/unibo/alchemist/boundary/composeui/Main.kt`

## Short version

If you are an AI agent and you need to work on `alchemist-composeui`, remember:

- preserve the shared state contract,
- keep platform code separated,
- test projection and interaction math,
- do not break the demo shell,
- do not overwrite UI-only toggles when refreshing live simulation data,
- keep control validation and popup state inside the shared store model.
Loading