diff --git a/CLAUDE.md b/CLAUDE.md index d14d0c1a2d..f8ef919576 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -186,6 +186,19 @@ A single `app.intent` YAML file at a project root is the source of truth one alt **The general platform line this enshrines:** authoring artifacts (`.edm`, `.model`, `.form`, `.report`, `.intent`) get **workspace editors + an explicit Generate**; only runtime artifacts (`.roles`, `.bpmn`, `.csvim`, `.table`, jobs, listeners, …) get **synchronizers**. Applying the synchronizer hammer to an authoring artifact generates into the registry where no modeler, Projects view, or template can use it — that mistake was made once and reverted; the inventory of synchronizers (grep `extends BaseSynchronizer`) deliberately contains no authoring formats. +## Harmonia runtime UI (`template-application-ui-harmonia-java` + `template-form-builder-harmonia`) + +A second runtime UI stack, parallel to the AngularJS/BlimpKit one: generated apps render as a self-contained **Alpine.js + Harmonia SPA** (client-routed by Pinecone in hash mode, no iframes/`postMessage` hubs), served at `/services/web//gen//index.html`, talking to the **reused** generated Java REST controllers over a `fetch` client. The AngularJS IDE is untouched; the two stacks coexist by URL. `template-application-ui-harmonia-java` (registered on `platform-templates` as "Application - UI (Harmonia) - Java") mirrors `template-application-ui-angular-java` and emits the view types (list, manage, setting, master-detail, reports) + built-in **Process Inbox** (`/inbox`) and **Documents** (`/documents`) shell sections + inline process-task surfacing; `template-form-builder-harmonia` ("Harmonia Generator from Form Model", extension `form`) is the runtime form generator. The whole stack — Alpine 3.15.11, Harmonia 1.24.1, Lucide 1.8.0, chart.js 4.4.3 — is embedded as **webjars** via `components/resources/application-core` (Pinecone Router has no webjar, so it is vendored under `application-core/.../vendor/`, license-excluded). Developed on PR [#6078](https://github.com/eclipse-dirigible/dirigible/pull/6078). + +**Detailed guides:** [`components/template/template-application-ui-harmonia-java/README.md`](components/template/template-application-ui-harmonia-java/README.md) (the SPA shell, view-type parity checklist, the master-detail detail registry, process-tasks store) and [`components/template/template-form-builder-harmonia/README.md`](components/template/template-form-builder-harmonia/README.md) (the neutral `formController(ctx)` contract). The repo-root [`HARMONIA_RUNTIME_PLAN.md`](HARMONIA_RUNTIME_PLAN.md) is the design doc + implementation status. **Gotchas that already burned someone — read before changing the templates:** + +- **REST path must use the Java-sanitised names.** `restBase` is `/services/java//gen/${javaGenFolderName}/api` and each page's `apiPath` is the **relative** `/${javaPerspectiveName}/Controller`; the fetch client prepends `restBase` exactly once. Use `javaGenFolderName`/`javaPerspectiveName` (e.g. `sales-order` → `sales_order`), **not** the raw `genFolderName`/lowercased perspective — the generated backend lives under the sanitised Java package. (Phase 0/1 only passed because `edm` needs no sanitising.) +- **`{ baseUrl: '' }` means "URL is absolute, prepend nothing".** The fetch client checks `opts.baseUrl !== undefined`, not truthiness — passing `''` with a relative path (or omitting it with an absolute path) is the classic doubled-URL bug (`/api/services/java/.../api/...`). Entity pages use a relative `apiPath` and **no** override; absolute URLs (relationship dropdowns, the detail registry) pass `{ baseUrl: '' }`. +- **Date/time widgets need conversion both ways.** The form `toPayload()` turns an HTML `date`/`datetime-local` value into a full ISO instant (`…Z`) so a Jackson `java.time.Instant`/`Timestamp` field binds (empty→`null`, a bare `TIME` passes through); `toDateInput()` slices the backend's ISO value back to what the widget expects on edit. Mirrors the AngularJS stack's `new Date(value)`. +- **Master-detail is registry-driven.** A master page renders one `detailPanel` per `App.detailsFor()` entry; each detail self-registers via `App.registerDetail(...)` (relative `apiPath`), so masters never enumerate details at generation time. The detail list filters via the controller's `?=` query (built into the reused rest-java controller for `*_DETAILS` layouts). +- **The `.form` `code` is framework-neutral now.** `template-form-builder-harmonia` runs the `.form` `code` as the body of `formController(ctx)` (`ctx.{model, params, http, task, notify, close}`), **not** AngularJS `$scope`/`$http`. Existing AngularJS `.form` code must be migrated; `FormIntentGenerator` emitting the neutral form is a follow-up. +- **Intent glue handlers are self-describing `@Component`s, not class-level `@Listener`/`@Scheduled`.** Those SDK annotations are `@Target(METHOD)`; the rollup/notification/integration/job templates in `template-application-events-java` were converted to `@Component implements MessageHandler/JobHandler` with `destination()`/`kind()`/`cron()` (matching the Trigger template) — class-level use fails `javac` with "annotation interface not applicable". + ## Native applications (`engine-native-apps`) User projects can declare a `*.nativeapp` JSON file that turns an external web server — local OS process or remote HTTP(S) endpoint — into a first-class Dirigible artefact, reverse-proxied under `/services/native-apps-proxy/v1//...` with optional Dirigible-managed authentication and role-based access. The whole feature lives in `components/engine/engine-native-apps`. diff --git a/HARMONIA_RUNTIME_PLAN.md b/HARMONIA_RUNTIME_PLAN.md new file mode 100644 index 0000000000..05cf0b6f61 --- /dev/null +++ b/HARMONIA_RUNTIME_PLAN.md @@ -0,0 +1,421 @@ +# Dirigible runtime on AlpineJS + Harmonia — research & plan + +Research and implementation plan for a parallel, fully-embedded runtime UI stack. +Framework swap only; behaviour parity. + +## Goal + +Keep the **IDE** (Workbench, Monaco editors, entity/form/intent modelers) on **AngularJS + BlimpKit**. +Move the **runtime** — generated *and* custom apps, plus their **shell/dashboard**, views, forms and +BPM task forms — onto a self-contained **AlpineJS + Harmonia** stack, with SAP-icons replaced by +Harmonia/Unicons, and the whole stack **built into the fat jar** exactly as AngularJS + BlimpKit is +today. No new behaviour — same flows, same REST backend, same artifacts. + +## Verdict + +Feasible, and the architecture already permits it — but it is a large, multi-phase initiative, not a +re-skin. The single biggest lever is that the platform's view↔shell contract is a **framework-agnostic +message protocol** (`postMessage` hubs) plus **JSON / plain-JS registration**. Keep that protocol as +the invariant and you can swap *both ends* (shell and views) to Harmonia while the IDE keeps running on +Angular — and the two stacks can coexist during transition. + +The real work is in three places: (1) reimplementing the runtime **shell/renderers** (the *receiving* +end of the hubs) in Harmonia, (2) a full **view template set** at parity, and (3) a Harmonia **forms** +runtime. Plus embedding + icons + a couple of genuine Harmonia gaps (split panes). + +> **Update after studying the reference implementation.** Two assumptions above are now corrected by a +> working sample (see the new section **"Reference implementation: codbex-athena-app"** below): +> Harmonia **does** ship a split component (`x-h-split`) — the split-pane gap is closed — and a full, +> reusable **Harmonia runtime dashboard/shell already exists** in that repo (sidebar + toolbar + +> breadcrumb + responsive collapse + theming + a `fetch`-based entity client + a 422 error-mapping form +> base). That collapses the single biggest cost (Workstream 3 / Phase 1) from "build from scratch" to +> "adopt and adapt." It also reveals a **different shell architecture** than this plan assumed — a +> single-page, client-side-routed SPA served as a plain Dirigible web resource, **not** the +> iframe-per-view + `postMessage`-hub seam. Read that section before committing to the phasing. + +## Reference implementation: codbex-athena-app + +`github.com/codbex/codbex-athena-app` (the `codbex-athena-app/` project inside it) is a **complete, +working Alpine + Harmonia runtime application** built exactly the way this initiative needs — and per the +direction *its dashboard/shell can be adopted directly*, not re-derived. It is a Dirigible project +(`project.json` with a `guid` + git dependency on `codbex-invoices`; served as a web resource under +`/services/web/codbex-athena-app/`) and it ships, in its own `.claude/skills/`, an 88 KB Harmonia +component reference and an Alpine-architecture guide that together encode the conventions below. Treat +this repo as the canonical pattern source for Workstreams 1–5. + +### What it proves / changes vs. the original plan + +1. **Split panes are NOT a gap.** Harmonia ships `x-h-split` + `x-h-split-panel`, and athena uses it for + the **shell layout itself** — the sidebar/main division is a horizontal split. Real usage from its + `index.html`: + ```html +
+
… sidebar …
+
… main …
+
+ ``` + Documented attributes: `data-orientation="horizontal|vertical"` (h is default), `data-default-size` + (percentages, panels should sum to 100), `data-min` / `data-max` (px clamps), `data-locked="true"` + (non-resizable pane), `data-gutterless="true"` (hide the drag handle), `data-variant="border"`, + `:data-hidden` (collapse a pane reactively). This covers **both** uses the plan flagged — master-detail + views *and* shell layout — so the `platformSplit` port is unnecessary. Decision in Phase 1 is resolved: + use `x-h-split`. + +2. **The runtime dashboard/shell already exists and is reusable.** `js/components/layout/appShell.js` is + a single `Alpine.data('app', …)` component providing everything Workstream 3 lists: sidebar nav + (`x-h-sidebar` + `x-for` over a `nav` array), a top `x-h-toolbar` with `x-h-breadcrumb` trail derived + from the route, a notifications popover, an avatar `x-h-menu` with a light/dark/auto theme submenu, and + **responsive collapse** driven by `Harmonia.getBreakpointListener((isNarrow)=>…, 1024)` that physically + re-parents the sidebar into an `x-h-sheet` drawer below 1024 px. Adopt this file as the starting shell; + the per-app parts that change are just the `nav`, `routeTitles`, and `sections` maps. + +3. **The shell architecture is a client-routed SPA, not the iframe+hub seam.** This is the biggest + architectural divergence from the plan. athena has **no iframes and no `postMessage` hubs**. It is one + HTML page; **Pinecone Router** (v7.5.0, **hash mode**, `basePath: '/services/web/'`) declares + routes as `