Package io.jmix.aitoolsflowui.view.chat
Class AiChatFragment
java.lang.Object
com.vaadin.flow.component.Component
com.vaadin.flow.component.Composite<com.vaadin.flow.component.orderedlayout.VerticalLayout>
io.jmix.flowui.fragment.Fragment<com.vaadin.flow.component.orderedlayout.VerticalLayout>
io.jmix.aitoolsflowui.view.chat.AiChatFragment
- All Implemented Interfaces:
com.vaadin.flow.component.AttachNotifier,com.vaadin.flow.component.DetachNotifier,com.vaadin.flow.component.HasElement,com.vaadin.flow.component.HasStyle,FacetOwner,FragmentOwner,Serializable
@JsModule("./aitools/ai-chat-timeline-scroll.js")
public class AiChatFragment
extends Fragment<com.vaadin.flow.component.orderedlayout.VerticalLayout>
Encapsulates the "AI chat panel" UI — title row, message timeline, thinking
indicator and the
AiChatInputFragment composer — for an AiConversation.
Designed to be embedded into any host view (the standard detail view, a
side dialog, a chat hub view, custom layouts).
Ownership. The host supplies the conversation to display via
setConversation(AiConversation) or setConversationId(UUID);
the fragment loads that conversation's AiChatMessages itself — it does
not rely on the messages collection being pre-fetched by the host —
and reads/writes directly through DataManager.
- See Also:
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic classFired when the title of the displayed conversation changes: a conversation is bound to the fragment, or the bound conversation is renamed.Nested classes/interfaces inherited from class io.jmix.flowui.fragment.Fragment
Fragment.ReadyEvent -
Field Summary
FieldsModifier and TypeFieldDescriptionprotected @Nullable TimelineItemprotected @Nullable com.vaadin.flow.function.SerializableSupplier<com.vaadin.flow.component.Component> protected AssistantResponseTaskCoordinatorprotected AiConversationAutoTitleServiceprotected booleanprotected AiChatServiceprotected booleanprotected com.vaadin.flow.component.orderedlayout.VerticalLayoutprotected AiChatInputFragmentprotected @Nullable AiConversationprotected AiConversationServiceprotected com.vaadin.flow.component.html.H3protected @Nullable com.vaadin.flow.component.shared.Tooltipprotected DataManagerprotected Dialogsprotected JmixButtonprotected MessageBundleprotected AiChatMessageServiceprotected Notificationsprotected AiToolsFlowuiPropertiesprotected booleanprotected TimelineItemFactoryprotected CollectionContainer<TimelineItem> protected JmixVirtualList<TimelineItem> protected TimeSourceprotected @Nullable AiConversationTitleModeprotected ConversationTitleSupportFields inherited from class io.jmix.flowui.fragment.Fragment
fragmentActions, fragmentData, fragmentFacets, parentController, uiComponents -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptioncom.vaadin.flow.shared.RegistrationaddTitleChangeListener(com.vaadin.flow.component.ComponentEventListener<AiChatFragment.TitleChangeEvent> listener) Adds a listener notified whenever the displayed conversation title changes — when a conversation is bound and after it is renamed.protected voidappendThinkingStatusUpdate(@Nullable AiToolStatusUpdate statusUpdate) protected voidprotected voidapplyAutoTitle(String trimmedMessage) Titles the conversation by its first message according to the effectiveAiConversationTitleMode.protected voidapplyConversationTitleText(@Nullable String title) protected @Nullable StringapplyFirstMessageTitle(String trimmedMessage) protected TimelineItemStatuscreateTimelineStatus(AiToolStatusUpdate statusUpdate) protected AiChatMessagecreateTransientAssistantMessage(String content) voidMoves keyboard focus to the message composer.protected void@Nullable com.vaadin.flow.function.SerializableSupplier<com.vaadin.flow.component.Component> Returns the custom assistant avatar icon supplier, if one was set.protected voidhandleAssistantResponseDone(@Nullable AiChatMessage finalMessage) booleanWhether an assistant response is currently being generated for the bound conversation.protected @Nullable AiConversationloadConversation(UUID conversationId) protected Collection<AiChatMessage> Loads all messages of the bound conversation, oldest first.protected voidmarkFreshAssistantItem(UUID assistantMessageId) Locates the assistant item that just landed in the container after the post-LLM reload and stamps it with the "fresh" flag, triggering a single-row re-render viaCollectionContainer.replaceItem(E).voidonEditConversationTitleBtnClick(com.vaadin.flow.component.ClickEvent<JmixButton> event) voidonReady(Fragment.ReadyEvent event) protected voidprotected voidprocessUserMessage(AiChatMessage savedUserMessage) protected voidreflectGeneratedTitle(UUID conversationId, String expectedTitle, @Nullable String appliedTitle) Shows the generated title, which is already persisted, unless the fragment was detached or the conversation was renamed meanwhile.protected voidprotected voidprotected voidprotected voidprotected AiConversationTitleModeprotected voidScrolls the timeline to the latest row, forcing the move regardless of where the user is currently looking.protected voidscrollToBottom(boolean force) Scrolls the timeline to the latest row.voidsendMessage(@Nullable String userMessage) Programmatic entry point to send a user message: persists it, appends it to the timeline, shows the thinking indicator, disables the composer and runs the assistant.voidsetAiAvatarIconSupplier(@Nullable com.vaadin.flow.function.SerializableSupplier<com.vaadin.flow.component.Component> avatarIconSupplier) Sets a supplier of the avatar icon shown next to assistant messages, letting the host override the default add-on icon.voidsetConversation(@Nullable AiConversation conversation) Binds the fragment to a conversation.voidsetConversationId(@Nullable UUID conversationId) Binds the fragment to a conversation by id.voidsetMessageInputEnabled(boolean enabled) Enables or disables the message composer.voidsetReadOnly(boolean readOnly) Hides the composer and the title-edit button.voidsetTitleMode(@Nullable AiConversationTitleMode titleMode) Sets the generation title mode for this chat.voidsetTitleVisible(boolean titleVisible) Hides the conversation title, for a host that already names the conversation in its own chrome — a view title, a tab, a dialog header — and would otherwise show the same name twice.protected voidprotected voidprotected voidstartTitleGeneration(String trimmedMessage, String expectedTitle) protected ValidationErrorsprotected voidMethods inherited from class io.jmix.flowui.fragment.Fragment
addReadyListener, findInnerComponent, getFragmentActions, getFragmentData, getFragmentFacets, getInnerComponent, getParentController, initContent, onAttach, onDetach, setFragmentActions, setFragmentData, setFragmentFacets, setParentController, setUiComponentsMethods inherited from class com.vaadin.flow.component.Composite
getChildren, getContent, getElementMethods inherited from class com.vaadin.flow.component.Component
addListener, bindVisible, findAncestor, fireEvent, from, get, getEventBus, getId, getListeners, getLocale, getParent, getTestId, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getTranslation, getUI, hasListener, isAttached, isTemplateMapped, isVisible, onEnabledStateChanged, removeFromParent, scrollIntoView, scrollIntoView, set, setElement, setId, setTestId, setVisible, whenAttachedMethods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, waitMethods inherited from interface com.vaadin.flow.component.AttachNotifier
addAttachListenerMethods inherited from interface com.vaadin.flow.component.DetachNotifier
addDetachListenerMethods inherited from interface io.jmix.flowui.facet.FacetOwner
getIdMethods inherited from interface com.vaadin.flow.component.HasStyle
addClassName, addClassNames, bindClassName, bindClassNames, getClassName, getClassNames, getStyle, hasClassName, removeClassName, removeClassNames, setClassName, setClassName
-
Field Details
-
dataManager
-
timeSource
-
dialogs
-
notifications
-
messageService
-
conversationService
-
assistantResponseTaskCoordinator
-
timelineItemFactory
-
chatService
-
properties
-
titleSupport
-
autoTitleService
-
messageBundle
-
conversationTitle
-
editConversationTitleBtn
-
composerContainer
-
timelineList
-
timelineItemsDc
-
composerFragment
-
conversationTitleTooltip
protected @Nullable com.vaadin.flow.component.shared.Tooltip conversationTitleTooltip -
conversation
-
activeThinkingItem
-
awaitingResponse
protected boolean awaitingResponse -
readOnly
protected boolean readOnly -
titleModeOverride
-
aiAvatarIconSupplier
protected @Nullable com.vaadin.flow.function.SerializableSupplier<com.vaadin.flow.component.Component> aiAvatarIconSupplier
-
-
Constructor Details
-
AiChatFragment
public AiChatFragment()
-
-
Method Details
-
setConversation
Binds the fragment to a conversation.- Parameters:
conversation- conversation to bind
-
setConversationId
Binds the fragment to a conversation by id. Anullid (or an id that no longer resolves) clears the fragment.- Parameters:
conversationId- conversation id to set
-
setAiAvatarIconSupplier
public void setAiAvatarIconSupplier(@Nullable com.vaadin.flow.function.SerializableSupplier<com.vaadin.flow.component.Component> avatarIconSupplier) Sets a supplier of the avatar icon shown next to assistant messages, letting the host override the default add-on icon. The supplier must return a fresh component on every call.- Parameters:
avatarIconSupplier- supplier of the assistant avatar icon, ornullto use the default
-
getAiAvatarIconSupplier
public @Nullable com.vaadin.flow.function.SerializableSupplier<com.vaadin.flow.component.Component> getAiAvatarIconSupplier()Returns the custom assistant avatar icon supplier, if one was set.- Returns:
- the avatar icon supplier, or
nullwhen the default icon is used
-
setReadOnly
public void setReadOnly(boolean readOnly) Hides the composer and the title-edit button.- Parameters:
readOnly-trueto hide the composer and the title-edit button
-
setTitleVisible
public void setTitleVisible(boolean titleVisible) Hides the conversation title, for a host that already names the conversation in its own chrome — a view title, a tab, a dialog header — and would otherwise show the same name twice. The title row itself stays, carrying the title-edit button.AiChatFragment.TitleChangeEventfires either way, so a hidden title still keeps the host's own title in sync with renames. The fragment stores no flag of its own, so the choice survives conversation reloads.- Parameters:
titleVisible-falseto hide the conversation title; visible by default
-
addTitleChangeListener
public com.vaadin.flow.shared.Registration addTitleChangeListener(com.vaadin.flow.component.ComponentEventListener<AiChatFragment.TitleChangeEvent> listener) Adds a listener notified whenever the displayed conversation title changes — when a conversation is bound and after it is renamed. Hosts use it to keep their own title in sync (page title, tab title).- Parameters:
listener- listener to add- Returns:
- a registration for removing the listener
-
setTitleMode
Sets the generation title mode for this chat. Overrides the application-leveljmix.aitools.ui.conversation-title-mode.- Parameters:
titleMode- the title mode for this chat, ornullto use the application-level setting
-
focusMessageInput
public void focusMessageInput()Moves keyboard focus to the message composer. No-op if the composer is currently disabled (e.g. while the assistant is still generating a response). -
setMessageInputEnabled
public void setMessageInputEnabled(boolean enabled) Enables or disables the message composer. The fragment disables it automatically while an assistant response is in flight and re-enables it on completion; hosts can call this for additional read-only states.- Parameters:
enabled-trueto enable the composer,falseto disable it
-
onEditConversationTitleBtnClick
@Subscribe(id="editConversationTitleBtn", subject="clickListener") public void onEditConversationTitleBtnClick(com.vaadin.flow.component.ClickEvent<JmixButton> event) -
isAwaitingResponse
public boolean isAwaitingResponse()Whether an assistant response is currently being generated for the bound conversation. Hosts can use this to warn the user before closing — the background task is scoped to the host view and is cancelled (losing the answer) when the view detaches.- Returns:
truewhile an assistant response is being generated
-
sendMessage
Programmatic entry point to send a user message: persists it, appends it to the timeline, shows the thinking indicator, disables the composer and runs the assistant. Used by the composer's submit handler and by hosts that start a conversation with an initial prompt (the chat-hub flow).- Parameters:
userMessage- message text to send; anullor blank value is ignored
-
onReady
-
applyAutoTitle
Titles the conversation by its first message according to the effectiveAiConversationTitleMode.- Parameters:
trimmedMessage- the trimmed first user message
-
applyFirstMessageTitle
-
startTitleGeneration
-
reflectGeneratedTitle
protected void reflectGeneratedTitle(UUID conversationId, String expectedTitle, @Nullable String appliedTitle) Shows the generated title, which is already persisted, unless the fragment was detached or the conversation was renamed meanwhile.- Parameters:
conversationId- id of the conversation that was titledexpectedTitle- first-message title the conversation must still carryappliedTitle- the generated title, ornullwhen nothing was applied
-
resolveTitleMode
-
processUserMessage
-
handleAssistantResponseDone
-
markFreshAssistantItem
Locates the assistant item that just landed in the container after the post-LLM reload and stamps it with the "fresh" flag, triggering a single-row re-render viaCollectionContainer.replaceItem(E).- Parameters:
assistantMessageId- id of the assistant message to mark as fresh
-
showAssistantProcessingError
protected void showAssistantProcessingError() -
showThinkingIndicator
protected void showThinkingIndicator() -
removeThinkingIndicator
protected void removeThinkingIndicator() -
appendThinkingStatusUpdate
-
refreshAll
protected void refreshAll() -
applyConversationTitleText
-
loadMessages
Loads all messages of the bound conversation, oldest first. Empty when no conversation is bound or it has not been persisted yet.- Returns:
- the bound conversation's messages, oldest first; empty if none
-
forceMessageInputFocus
protected void forceMessageInputFocus() -
refreshComposerVisibility
protected void refreshComposerVisibility() -
appendTimelineItem
-
refreshTimelineItem
-
scrollToBottom
protected void scrollToBottom()Scrolls the timeline to the latest row, forcing the move regardless of where the user is currently looking. Use for discrete, user-initiated events (opening a conversation, sending a message, the assistant's final answer) where the bottom must be shown. -
scrollToBottom
protected void scrollToBottom(boolean force) Scrolls the timeline to the latest row.- Parameters:
force- whentrue, always scrolls to the bottom and resets the "stick to bottom" intent. Whenfalse, scrolls only if the user is already at (or near) the bottom — used for streaming status updates so a user who scrolled up to read is not yanked back down on every minor update.
-
createTransientAssistantMessage
-
createTimelineStatus
-
loadConversation
-
openTitleEditDialog
protected void openTitleEditDialog() -
validateTitle
-