Skip to content

Move the HTML UI toolkit into a library of its own #258

Closed
10 of 12 issues completed
Closed

Description

@adam-waldenberg

Janus renders its UI as HTML, serves it on a loopback port and shows it in an embedded Chromium (JCEF). None of that is wallet-specific, and it has turned out to be a nice way to build a Java desktop app, so let's pull it out into a library other projects can use.

How it works today

The page posts to /action/<name> through htmx. A method annotated @Action("<name>") on a bean answers: it takes nothing or a Form, returns a View that names a Thymeleaf fragment, and the rendered fragment gets swapped into the page. Return nothing and the page stays as it is. ActionExtension picks the methods up while CDI starts, so adding an action is just adding a method.

What to do

  • Move the generic parts of web and shell into a new repo: server and routing, @Action dispatch, Guard/SessionToken, the JCEF window and engine handling, and the page scripts (htmx, window.js, failure.js). core, the controllers, views, templates, styling and the installers stay here.
  • Publish releases to GitHub Packages so Janus can depend on a version from its Maven build. The web and shell modules go away.
  • Strip out what is Janus-specific: the window title, icon, minimum size, janus.* properties and the Unigrid command name, the ~/.janus folder, the fixed templates/ and static/ roots, and the markup the scripts assume (.card, .step__error). All of that has to come from the application.
  • Document it: a README with a minimal app, a guide to actions, views, window control and engine location, and Javadoc on everything public.
  • Add a small sample app in the library repo that runs from a clean checkout.

Design

I'd keep @Action as a plain method that returns its result. It reads well, a unit test just calls it, one method answers one name, and the return type can grow later (redirect, download). I looked at doing it with CDI events instead and don't think it fits: observers are void and broadcast, so the view would have to be set on the event and we'd have to police that only one observer answers.

Worth adding: @Param("passphrase") String passphrase so form values arrive as parameters, and CDI events the app can observe for lifecycle (ServerStarted, WindowOpened, WindowClosing, FileChosen) rather than the toolkit calling into it. Actions.of stays so a controller can be tested without a container.

Tests are jqwik, as here. @UiTest would be a jqwik lifecycle hook that boots the container and server around each try (Playwright on request) and hands the test a Client, Page or browser Page as a parameter. The test-jar should ship arbitraries for action names, form fields, tokens and file paths, so "nothing the page can post makes an action answer 500" is a @ForAll away.

Open

  • Name. Casement, Oriel, Skylight?
  • One artifact or two (server, window host)?

Activity

  1. changed the issue type fromtoon Oct 8, 2026
  2. modified the milestones: 2.0.1, 2.1.0 on Oct 8, 2026
  3. adam-waldenberg commented on Oct 9, 2026

    @adam-waldenberg
    ContributorAuthor

    Too big for one change. Let's split it into sub-issues as we go (the move, the de-Janus work, test support, docs, sample app) and keep this one as the overview.

  4. modified the milestones: 2.1.0, 2.0.1 on Oct 26, 2026
  5. adam-waldenberg commented on Oct 29, 2026

    @adam-waldenberg
    ContributorAuthor

    Done in #259, now on master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

documentationImprovements or additions to documentationenhancementNew feature or request

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions