Skip to content

The VCL component ​

TVclHelpChatAI (unit Vcl.HelpChatAI, package HelpChatAIVcl) is a non-visual component: drop it on a form, set Active := True, and it takes care of the bubble, F1, the screen context and the chat panel. The panel itself is TVclHelpChatPanel, a control hosting a WebView2 (TEdgeBrowser) — you normally never touch it directly.

Activating it ​

pascal
HelpChatAI.Active := True;

Activation starts the engine and the local HTTP server that serves the chat page, registers the component as the help viewer of the application, and places the bubble on the host form. Setting Active := False (or freeing the component) stops the workers with a bounded wait, closes the server and saves the conversation if persistence is on.

Active set at design time is streamed and applied when the form is loaded at runtime.

Opening the chat ​

MethodWhat it does
OpenOpens the panel with the context of the current screen
OpenForCurrentScreenOpens it and asks the localized question about this screen (what F1 does)
OpenForHelp(AKeyword, AContext)What the help system calls: the current context, with that keyword or context id
OpenWithQuestion(AText, ASend)Opens it and sends AText (or just pre-fills it when ASend is False)
Close / Toggle / IsPanelVisibleThe rest of the window management

OpenWithQuestion is how you add your own entry points — a menu item "Ask the assistant about invoices", a button in a toolbar:

pascal
HelpChatAI.OpenWithQuestion('Come si registra una fattura di acquisto?');

The same question asked twice within 1.5 seconds is shown but sent once: a key repeat or a request routed twice cannot spend two model calls.

Where the chat appears ​

By default the panel lives in a small tool window anchored to the bottom-right corner of the host form, which remembers where the user left it (RememberPanelPosition, per application and per user).

Embedding the chat in your own window ​

Everything visible is inside the browser, so what an application really decides is where the chat appears. Give it any TWinControl and it fills that instead:

pascal
procedure TMainForm.HelpChatBeforeShowHelp(Sender: TObject; var AContainer: TWinControl);
begin
  if EmbeddedCheck.Checked then
  begin
    ChatPanel.Visible := True;    // an alRight TPanel of this form
    ChatSplitter.Visible := True;
    AContainer := ChatPanel;
  end;
  // leaving AContainer nil keeps the component's own window
end;

procedure TMainForm.HelpChatBeforeHideHelp(Sender: TObject);
begin
  ChatPanel.Visible := False;     // the user closed the chat: hide your container
  ChatSplitter.Visible := False;
end;
EventWhen
OnBeforeShowHelp(Sender; var AContainer)Before showing: arrives with the Container property, set it to your control
OnAfterShowHelpThe chat is visible in its container
OnBeforeHideHelpThe user closed it (✕, Esc, Close): hide your container here
OnAfterHideHelpAfter hiding

The Container property does the same thing without an event, when the place never changes. Remembering size and position is the job of whoever hosts the chat: the component's own window does it, a container of yours is yours to persist.

The bubble and F1 ​

ShowBubble puts a round button with the assistant icon in the bottom-right corner of HostForm (or Application.MainForm when not set). HandleF1 and RegisterHelpViewer decide what F1 does — the full story is in F1 and the screen context.

The bubble is a control with a window of its own, and that is not a detail. It used to be a TSpeedButton, and on a real form it vanished: a graphic control is painted on the canvas of its parent and the VCL keeps it below every windowed control of the same parent, so a grid, a page control or a panel over that corner simply hid it, and BringToFront could not help. The one you get now raises its window and asks to be raised again whenever the form adds a control or is resized.

Changing the icon ​

The usual Delphi contract, on the component:

PropertyWhat it does
ImagesYour image list. Leave it nil to use the icons shipped with the component
ImageIndexThe icon by position
ImageNameThe icon by name, which is what the built-in list expects

In the Object Inspector both ImageIndex and ImageName drop down the icons themselves, drawn: the editor the IDE gives the standard controls is not offered to a component that is not one of them, so the library registers its own.

pascal
HelpChatAI.ImageName := 'AIAssistant_Female';

Four icons are compiled in: AIAssistant_Male and AIAssistant_Female, the assistant with the chat bubble and its spark, and Assistant_Male, Assistant_Female, the same faces without it. Each ships in nine sizes from 16 to 192 pixels, and the bubble picks through a TVirtualImageList: having a window it knows its own DPI, so a 150% or 200% monitor gets a crisp icon instead of a stretched one. BubbleColor on the control changes the colour of the disc; clNone follows the VCL style.

Naming the assistant ​

Two properties decide what the user reads, and both are empty by default - which does not mean "nothing", it means "the word of the current language":

PropertyEmpty gives youWhat it is
CaptionHelp (Aiuto in Italian)The title of the window the chat opens in
AssistantNameAssistant (Assistente)The name in the header of the panel, and the title of the page
pascal
HelpChatAI.Caption := 'Assistenza clienti';   // the window
HelpChatAI.AssistantName := 'Aida';           // the header of the chat

The header draws the icon of the bubble to the left of the name, so the assistant looks the same wherever the user meets it: change Images, ImageIndex or ImageName and both follow. The icon reaches the page as a data: URI the component renders from your image list - nothing is fetched over the network, and the content policy of the page allows no other source for an image.

Both properties can be changed while the panel is open; the header updates without reloading the conversation. Leave them empty unless you have a reason: an application translated into a language the library knows gets the right word for free, and a name written in the Object Inspector is the same in every language.

The window is not the assistant

Caption is a window title - the user reads it in the taskbar and in Alt+Tab, so "Help" says what the window is for. AssistantName is a name, and it belongs inside. When the chat is embedded in a container of yours there is no window, so only AssistantName shows.

Trying it at design time ​

Right-click the component on the form, or double-click it:

VerbWhat it does
Test HelpChatAI...Opens the real chat in a modal window and lets you ask it something
Check configuration...Answers "would this work?" without building the application
Ver. x.y.z - Open Web Help...Opens this documentation

The preview is the chat, not a mock-up: the engine, the local server and the WebView2 page are the ones the application will run. Three things differ at design time, and the component editor deals with all three rather than leaving them to surprise you:

  • relative paths resolve against the folder of the running executable, which here is the folder of bds.exe. Settings.DocIndexFile, Settings.DocFolder and Settings.ConversationFile are resolved against the output folder of the active project instead, and the configuration is read the way that application would read it;
  • WebView2Loader.dll is not next to bds.exe. It is looked for in the output folder of the project — where the application needs it anyway — and in Redist\WebView2 under the folder the IDE knows as $(HELPCHATAIDIR);
  • the component on the form is never started. It stays in csDesigning: what runs is a copy of it, owned by nothing, freed when the preview closes, with the bubble, the help viewer and F1 switched off. Actions are off too, because their handlers live in the application that has not been built.

Only the providers compiled into the library run inside the IDE. One that your application registers exists once that application runs, and the check says so instead of calling it a typo.

The Object Inspector ​

Four properties behave differently from what the stock editors would give you:

PropertyWhat you get
Settings.ProviderA drop-down of the providers registered in the IDE, instead of a free string
ConfigFileNameA file dialog behind the ellipsis
ContainerOnly the controls that can actually hold another one, not every TWinControl of the form
ImageIndex, ImageNameThe icons themselves, drawn in the list; with no Images of yours, the four built-in ones

HostForm is not published at all: at design time the form of another unit is not a component of this one, so the combo would have been permanently empty. Set it in code.

The library also puts itself in the splash screen and in the About box of the IDE, with its version, so you can tell which build is installed.

Dictating the question ​

A microphone sits left of the question box: press it and talk, press it again to stop. Sending the question stops it too. The text lands in the box and stays there, so the user rereads it and sends it: nothing leaves on its own.

VoiceEngine chooses who listens, and what you choose is what runs — an engine that does not work says so, it is never swapped underneath:

ValueWhat you get
hveNoneNo dictation: nothing is asked of Windows, no microphone button
hveSystemDictation (default)The dictation of Windows, the one Win+H starts. Windows types into the box itself, so the component sees nothing and only knows that it asked
hveRecognizerWindows.Media.SpeechRecognition: the box fills while the user talks, the button pulses, OnVoiceText sees every piece before it lands

The dictation is the default because it works wherever Win+H works. The recognizer is the better citizen, since the text passes through your code, but it needs the online speech recognition consent of Windows, and on recent Windows 11 builds it can start, hear the voice and transcribe nothing at all. No application can give that consent: with hveRecognizer the component checks it, and the first time it is missing it explains what is needed and opens that page of the Windows settings. Handle OnVoiceConsentNeeded to say it your own way.

pascal
procedure TMainForm.HelpChatVoiceText(Sender: TObject; var AText: string;
  const AFinal: Boolean);
begin
  if AFinal then
    AText := MyGlossary.Fix(AText);   // "vi ci el" -> "VCL"
end;

In Delphi 10.4 only hveSystemDictation exists, because the RTL translates those WinRT APIs from Delphi 11 on: ask for the recognizer there and the button stays hidden.

Colours ​

With FollowVclStyle (the default) the panel is coloured after the active VCL style and follows a style change at runtime, including while the chat is open. OnGetTheme lets you adjust the result — a brand accent, a different font. See Colours and theming.

Actions ​

Actions is the registry of what the assistant may do in your application, available from the component's constructor:

pascal
HelpChatAI.Actions.Add('open_screen', 'Brings a screen of the application to the front.',
  [THelpChatActionParam.Create('name', 'Screen name')],
  procedure(const AArgs: THelpChatActionArgs; var AResult: string)
  begin
    PageControl.ActivePage := FindTab(AArgs.AsString('name'));
    AResult := 'Opened the ' + ... + ' screen.';
  end);

See Actions for the whole picture, including confirmation.

Everything at a glance ​

The full list of properties, events and methods is in the TVclHelpChatAI reference.

What the component needs from you ​

  • WebView2Loader.dll next to the executable (see Installation).
  • A HelpKeyword on the screens you want the assistant to recognise.
  • Nothing else: no form to design, no thread to manage, no message loop hook.

One component per application

The component registers a help viewer and hooks the application's message loop. Two active instances in the same process would both answer F1. Put it on the main form.

Released under the Apache License 2.0