Migration of the Harmonia UI library consumed by the Dirigible platform from
1.24.2 to 2.1.0. This document is the working plan; keep it in sync as
phases land.
- Version pin:
pom.xml<harmonia.version>=2.1.0. - The webjar is a dependency of exactly one module -
components/resources/application-core(org.webjars.npm:codbex__harmonia), served from the classpath at/webjars/codbex__harmonia/dist/.... No dist files are vendored; the shell runtime is shared, not copied per project. - Consumers:
components/template/template-application-ui-harmonia-java(bulk of thex-h-*usage),components/template/template-form-builder-harmonia,components/resources/application-core(shared shell runtime), andcomponents/resources/resources-application(live application shell). codbex__harmonia:2.1.0is published on Maven Central and resolves. The webjar keepsdist/harmonia.min.js+dist/harmonia.css(existing tags keep resolving) and ships the opt-indist/harmonia-lucide.*anddist/harmonia-i18next.*bundles plus askills/directory.
- 2.0.0 - the only release with breaking changes (see below).
- 2.0.1-2.0.3 - fixes; 2.0.3 was the first we adopted (Phase 1).
- 2.1.0 - NO breaking changes. Adds an opt-in i18next plugin, two app templates (Granite ERP, Onyx Chat), badge/avatar/sidebar enhancements, and a Lucide-plugin enhancement (see Phase 2). Safe to bump straight from 2.0.3.
- Date/time pickers refactored to use standard model values (
YYYY-MM-DDfor the date picker) instead of custom formats. - Icon rendering changed from a modifier to the reactive
data-iconattribute:x-h-icon.home->x-h-icon data-icon="home". - Utility class
absolute-fitrenamed toposition-fit. - Built-in icons renamed:
info.svg->circle-info.svg,warning.svg->circle-warning.svg. - Form validation now defers to
:user-invalidinstead of:invalid; opt back into on-load validation withdata-validate="immediate"on an ancestor.
- Native charts:
x-h-chart-{line,bar,doughnut,pie,scatter}. - Lucide lifecycle directive
x-h-lucide(opt-in bundle; drop-in fordata-lucide). - New components:
datetime-picker,file-upload,rating,slot-picker, inline calendar; enhanced popover/progress/sidebar/calendar/badge/avatar. - Locale-aware
x-h-date-format+ shared intl engine. - Opt-in i18next plugin (
x-h-translate,$t/$i18n) - the shell already has its own i18n, so this is not needed for the migration. - New utility classes/tokens:
col-span-*,row-span-*,line-clamp-*,position-fit/position-center,text-2xs,self-*,h-mask/v-mask,tabular-nums, extended sizing scales, standard palette. - Generated, shipped agent skill under
skills/harmonia/(package now ships it).
<harmonia.version>->2.1.0(was bumped to 2.0.3 first, now 2.1.0).- Migrated the Harmonia built-in icons from
x-h-icon.<name>to the reactivex-h-icon data-icon="<name>"form (34 sites). Icons that never existed in Harmonia were fixed:upload-> built-inimport;inbox/eye-> Lucide. (x-h-info-page-media.iconstill exists in 2.x - left as is.) - Refreshed the vendored
template-application-ui-harmonia-java/reference/harmonia/skill from the 2.1.0-shippedskills/harmonia/. (Since removed - the skill is now consulted upstream at https://github.com/codbex/harmonia/blob/main/skills/harmonia/SKILL.md / https://codbex.com/harmonia/ to avoid drift.) - Synced version strings in
CLAUDE.mdandHARMONIA_RUNTIME_PLAN.md(data-sizesplit note re-verified - still accurate in 2.1.0). - Also fixed a pre-existing bug found during runtime verification: the
standalone generated report page (
report-file/index.html.template) loadedreport.js(which formats viawindow.HarmoniaFormat) but neverformat.js, soHarmoniaFormatwas undefined in the report iframe. Added theformat.jsinclude; also droppeddeferonformat.jsin the shells.
Runtime-verified on a 2.x instance: 2.x loads, shell + generated Sales Invoice
app render, all migrated icons render as SVG, generated output carries the 2.0
data-icon form only.
Harmonia auto-persists the color scheme to localStorage and auto-syncs it across all same-origin iframes and other browser tabs/windows (see the Harmonia dark-mode skill). The custom plumbing is therefore redundant.
- Delete the custom cross-iframe
storagelisteners (generated shell template, plus the copies in thereport-fileand form-builder templates). - Keep exactly one color-scheme toggle, in the shell only (live
resources-application/.../application/index.htmltoolbar and the generatedui/shell/index.html.templatetoolbar). Remove any theme toggle/logic that leaked into embedded perspective iframes - the shell button propagates automatically. The toggle's sun/moon icon is a Lucide icon that flips with state, so render it with the<svg x-h-lucide>+x-showpattern (see below). - Slim
application-core/.../shell/js/stores/theme.jsto thin wrappers overHarmonia.getColorScheme()/setColorScheme()(optionallyaddColorSchemeListener); drop manual DOM/class/sync code. Reconsider the first-run "force light" bootstrap - Harmonia defaults toautoand persists the last choice.
Load the opt-in /webjars/codbex__harmonia/dist/harmonia-lucide.min.js bundle in
every shell/standalone page, AFTER the Lucide UMD (it needs window.lucide).
KEEP the Lucide webjar - Harmonia does not bundle Lucide. Then remove the manual
lucide.createIcons() / refreshIcons() wiring (x-h-lucide renders on init
and inside dynamically loaded fragments / x-for / x-if / router views).
Migration depends on whether the icon is static or changes at runtime. Two rules from the 2.1.0 Lucide plugin:
<i x-h-lucide>(or any non-svg tag) is REPLACED by the rendered<svg>. Combining it with any other Alpine directive (x-show,:class,@click, ...) now THROWS a descriptive error (the only exception is:data-lucide, whose bound name is consumed at render time). So a static<i role="img" data-lucide="home">just getsx-h-lucideadded.<svg x-h-lucide>is rendered IN PLACE, so Alpine directives on it survive. Use this form whenever the placeholder also carriesx-show/:class/ etc.- The icon name is read ONCE.
x-h-lucidedoes NOT re-render when a bound:data-lucidevalue changes. Any current:data-lucide="expr"+refreshIcons()toggle (e.g. the report chart/table buttontable-2<->bar-chart-3) must become either two<svg x-h-lucide>elements toggled withx-show, or an element re-created viax-if/templateso the directive runs again. A blinddata-lucide->x-h-lucidesweep would FREEZE these on their first value.
So, concretely:
- Static
data-lucideon a directive-free placeholder: addx-h-lucide(keep<i>). - Placeholder that also has another Alpine directive, or whose name toggles: use
<svg x-h-lucide>(+x-showfor a toggle pair), orx-if-recreate. - Remove
createIcons()/refreshIcons()calls and their@click="...; refreshIcons()"hooks.
Rule: if a Harmonia component exists, use it. No native-HTML or bespoke
substitutes. All picker models match the native <input> value they replace, so
the existing x-model="form.X" bindings and format.js.toDateInput() shapes stay
compatible (verify each on the running app).
-
Date ->
x-h-date-picker(compound: root +-trigger+-popup;x-modelon the popup). ModelYYYY-MM-DD(== nativetype="date"). -
Datetime ->
x-h-datetime-picker(root +-trigger+-popup;x-modelon the popup). Model ISO withTseparatorYYYY-MM-DDTHH:mm[:ss](== nativetype="datetime-local"). -
Time ->
x-h-time-picker(root +-input+-popup;x-modelon the input). ModelHH:MM[:SS], 24h (== nativetype="time").Sites (native
<input x-h-input type="date|datetime-local|time">today):document-view(form fields + inline draft rows),manage/form-view,report-file/indexfilters, form-builderui/index. -
Number: the correct pattern is
<div x-h-input-number><input type="number"></div>(already used in most places). Wrap the one bare<input type="number">inmanage/form-view.html.template. -
Sweep for any other native/bespoke control with a Harmonia equivalent (e.g. file upload ->
x-h-file-upload).
- Replacing
application-core/.../shell/js/services/format.jswith the 2.x intl engine. Reversed by #6193 (unify date/number formatting behind one configurable source): the custom module intentionally preserves JavaDecimalFormat/DateTimeFormatterpattern semantics (per-field scale, instance-level separators, dependency-free for the BPM iframe).x-h-date-formatisIntl-based and cannot reproduce those. At most, usex-h-date-formatfor pure-locale display where Java-pattern semantics are not required (marginal).
- Chart.js -> native
x-h-chart-*. The report/chart/dashboard/KPI stack was built very recently on Chart.js (#6137reports[].chart, #6133/#6136/#6140). Migrating means reworking fresh code, and two gaps stand:polarArea/radarhave no nativex-h-chart-*equivalent, andprintChart()canvas->PNG depends on a<canvas>the native components may not expose. The Chart.js webjar lives incomponents/resources/resources-resources/pom.xml(chart.js.versionin rootpom.xml); only droppable if both report surfaces fully migrate. Do this as a separate, coordinated PR. Done (#6590): the Harmonia reports run on the nativex-h-chart-*charts and the AngularJS report surface was removed with the Dashboard shell (#6589), so the Chart.js webjar and its version property are gone.
Build the platform (needs JAVA_HOME -> corretto-21; install the root pom first
if building modules with -pl, else ${harmonia.version} resolves from the
stale installed parent), launch an instance, and drive the Harmonia app shell + a
generated app (e.g. dirigiblelabs/sample-intent-multi-model): shell theme
toggle (verify iframe/tab propagation), list / manage / master-detail / document
perspectives, dialogs, forms (validation timing + pickers), split panels, reports
(icons + charts + number formatting). Watch the known Selenide UI IT flakiness
and the split-panel FOUC/data-size behavior.