Skip to content

Serve a Vaadin chat at /ui with a blocking reply - #98

Merged
arefbehboudi merged 2 commits into
ClawRunr:mainfrom
JoseWalker:feat/vaadin-01-chat
Oct 24, 2026
Merged

arefbehboudi merged 2 commits into
ClawRunr:mainfrom
JoseWalker:feat/vaadin-01-chat

Conversation

@JoseWalker

Copy link
Copy Markdown
Contributor

First step of the split we agreed on: the Vaadin dependency, a chat view at /ui, the existing
conversation from the agent's own chat memory, and a composer with a blocking (non-streaming)
reply. Streaming is the next MR.

What it does

  • New Gradle module ui, depending only on base; app depends on ui.
  • ChatView at /ui shows the web conversation — the same one the HTMX chat uses — read with
    ChatMemoryRepository.findByConversationId, user and assistant messages only, the same filter
    as ChatChannel.loadHistoryAsHtml. There is no second store: a message sent from /ui shows up
    at /chat and vice versa.
  • Send calls Agent.respondTo(conversationId, question) and appends the question and the reply
    as plain text. Markup in messages is displayed, never rendered.
  • A failing agent or an unreadable conversation shows a generic line on the page; the details go
    to the server log only.
  • /chat is untouched and / still redirects to it.

Worth knowing

  • Blocking. The agent call runs inside the Vaadin request, which holds the browser session's
    lock, so every /ui tab of that session waits until the reply is back, and the question only
    appears together with its answer. The streaming MR moves the call off the request thread.
  • Production mode in ui/build.gradle. A development-mode build only starts with Vaadin's dev
    server on the classpath, so the packaged jar and the Jib image would fail on startup. With no
    custom frontend, production mode uses Vaadin's pre-compiled bundle and needs no Node.
    bootRun runs in production mode too.
  • Lumo. Vaadin 25 applies no theme by default, so the stock Lumo theme is loaded through an
    AppShellConfigurator. JavaClaw's own look and light/dark are a later MR.
  • Layout. A centred reading column with the reader's messages on the right and the agent's on
    the left, like /chat, in styles/chat.css using only Lumo tokens. Without it the transcript
    ran edge to edge and looked worse than the page it will replace. Happy to drop it if you'd rather
    keep this MR even more bare.
  • History length. /ui shows what the chat memory holds — at most the last 20 messages,
    because of MessageWindowChatMemory's default window — exactly like /chat today.

Not in this MR

Streaming, Stop, Markdown, copy, edit/regenerate, theme, keyboard shortcuts, mobile layout, code
highlighting, conversation list and settings — each comes as its own MR, in that order.

Verified

  • ./gradlew clean build: all tests pass, including ModularityTests and ModuleBoundaryArchTest;
    8 new tests in ChatViewTest.
  • The packaged jar (java -jar app/build/libs/app-1.0.0-SNAPSHOT.jar) starts; /ui/ returns 200
    and / still redirects to /chat.
  • In the browser, with a real model: the existing conversation is shown, a question sent from
    /ui gets its reply, and the same exchange is visible at /chat.

JoseWalker and others added 2 commits October 22, 2026 10:14
First step of moving the chat from HTMX to Vaadin. The new view shows the
existing "web" conversation, read from the agent's own chat memory, and
sends a question through Agent.respondTo, showing the reply once it is
complete. Streaming is the next step. The HTMX chat at /chat is unchanged
and is still the start page.

The UI lives in its own Gradle module, ui, which depends on base. The ui
module builds Vaadin in production mode: a development-mode build only
runs with Vaadin's dev server on the classpath, so the packaged jar and
the Jib image would not start. With no custom frontend this uses Vaadin's
pre-compiled bundle and needs no Node. Vaadin 25 applies no theme by
default, so the stock Lumo theme is loaded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without a layout the transcript ran edge to edge with nothing telling the
speakers apart, which read as broken next to the HTMX chat. The reader's
messages now sit on the right and the agent's on the left, in a centred
reading column, as in /chat. Only Lumo tokens are used, so colours and
light/dark stay with the later theme step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@JoseWalker

Copy link
Copy Markdown
Contributor Author

CI failure here is unrelated to this change: it's WacliSyncTest.givesUpAfterTooManyFailuresInQuickSuccession in the WhatsApp plugin. WacliSync.start() schedules the first sync run with zero delay and only then resets failures = 0, so on a loaded runner the first run can count a failure before the reset wipes it, and the sync needs one restart more than the test expects (7 starts instead of 6). It passes locally 15/15; forcing the reset to land after two failures reproduces the exact failure at line 87, and resetting before scheduling fixes it. One-line fix, kept separate: #99.

@arefbehboudi
arefbehboudi self-requested a review October 23, 2026 18:14
@arefbehboudi

Copy link
Copy Markdown
Collaborator

Thanks @JoseWalker, this is exactly the scope I had in mind: small, isolated

@arefbehboudi
arefbehboudi merged commit 142f449 into ClawRunr:main Oct 24, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants