Table of Contents | ||||
---|---|---|---|---|
|
...
As indicated on the Project Background Knowledge page, the main purpose of providing visual support is twofold: to provide (1) support to a user to reduce their cognitive load (the amount of information working memory needs to process at any given time) and (2) an indication of the progress on the task that has been made thus far. Visual support will be provided by means of a webpage. This page aims at explaining how you can develop code for your MARBEL agent to create such a webpage.
To get a Quick Start into how to create a webpage in your MARBEL agent, you can also start by completing the Getting Your Conversational Agent Up and Running task first, and then get back to this page to better understand how you can develop a webpage in this project.
Note |
---|
Although visual support is key because of the reasons listed above, it is also only meant to be just that: support that facilitates conducting a conversation with your conversational agent. In this project, you should design conversational interaction to be the primary modality of interaction! Requirement: You should develop a webpage which facilitates conversational interaction; in other words, your webpage design should recognize that speech-based interaction is the primary modality of interaction. As such, you should avoid using input-based interactive webpage elements such as forms, dropdown menus, or other elements focused very much on user input via a webpage. |
...
Most HTML tag pairs can be simply generated by using a start and end tag and by adding some text to create the corresponding HTML. For example, <button>Start</Button>
can be used to generate a simple button saying Start. However, to unleash the full power of the HTML language, it is often useful to modify the type or style of an HTML element by adding additional attributes in the form of name="value"
to the start tag.
Making a button
For example, the following HTML code creates a large, colored button using the class
attribute and several button class values made available by Bootstrap:
...
When evaluating this clause, we would end up with Html
having the button HTML code as its value.
Besides the class
, style
, and data-value
attributes there are other global attributes in HTML. Because we think most of these have limited used we have not included these in the code provided to you. You are free, of course, to extend the Prolog code to be also able to use these other attributes.
'<span style="font-family:Roboto;">',
Generating an HTML webpage
By means of these and generate with the rule. The HTML code is represented in Bootstrap format, which is also clearly illustrated by examples below or on Bootstrap's documentation website (Bootstrap Documentation).
Note |
---|
Prolog Advice: To manipulate strings and atoms in Prolog it is useful to look at documentation of the following built-in functions: atomic_list_concat, atom_concat, string_concat, append, and maplist here: https://www.swi-prolog.org/. The predicate applyTemplate is a defined predicate that will be explained below. |
Prolog rules are used to add a condition to a webpage (i.e. webpage X is shown when Clause Y is true).
A Quick Prolog Walkthrough
Using Prolog rules in the html.pl
file
You add a condition that checks for a particular pattern ID to a Prolog rule in the html.pl
file. Other simple conditions that you can add involve counting the number of recipes that are still available while in the recipe selection phase, etc. The general form of the page lay-out rules would be something like:
Code Block |
---|
myPage(Txt, Button, Html) :-
<HERE YOUR CONDITION FOR SHOWING THIS PAGE>,
% e.g. while recipe is being selected (check for e.g. currently active top level pattern id), we'll display recipe features
% below code that specifies the page layout for this context
applyTemplate('<div class="card mx-auto" style="width:67vw">~a</div>, Text, Html),
…
. |
There are still many options to vary. For example, you can choose the parameters for the myPage
predicate that fit your approach best.
applyTemplate('Template String with ~a where you want to insert atom', Atom to Insert, Return Variable).
Example Prolog Rule for a Page
Code Block | ||
---|---|---|
| ||
page(c10, _, Html) :-
% Condition for when to show this page
CONDITION X,
% Constructing HTML page
atomic_list_concat(['<div class="alert alert-light"><center></br><h1>Hello!</br></br>','I am ~a</h1></br></br></center></div>'], Template),
% Get the bot's name if it has one; other call it 'your assistant'
(agentName(Name) -> N = Name ; N = 'your recipe selection assistant'), applyTemplate(Template, N, Body),
% Create the HTML page
html(Body, Html). |
A Quick Bootstrap Conceptual Walkthrough
Bootstrap is a useful framework for working out a display, but may not be so intuitive in combination with Prolog. We provide a short walkthrough here to explain things for you.
Let’s consider you would like to display a certain lay-out with three images side-by-side. The first step you would take is to check whether such a lay-out is specified in Bootstrap. ‘Card decks’ might be a nice option, so we first look into the code that is involved:
Code Block |
---|
<div class="card-deck">
<div class="card">
<img class="card-img-top" src=".../100px200/" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
<div class="card">
<img class="card-img-top" src=".../100px200/" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This card has supporting text below as a natural lead-in to additional content.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
<div class="card">
<img class="card-img-top" src=".../100px200/" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This card has even longer content than the first to show that equal height action.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
</div> |
What we want to achieve, is to render a page with the HTML code, but with a dynamic input of images. The trick is to break this down into fragments. First, we want to render a given image. In the HTML, we see that each of the three images are defined as separate cards. We will now use Prolog to generate the HTML for such a card, using a given picture URL. The aim is to connect in a single string the static HTML code and the image URL (which differs according to the context of the conversation). Let’s work with the good old Pasta Aglio URL for this example. We will define a predicate ‘imgCard’, with an arity of 1 and an arity of 2. We will give it once as a fact, specifying the HTML code:
Code Block |
---|
imgCard('<div class="card"><img class="card-img-top" src="~a" alt="Card image cap"></div>'). |
Note that the HTML is copied, with a replacement of the contents of ‘src’, in the form of ‘~a’ which is a placeholder for the image URL. This is needed in order to dynamically add this information. We will do this with the following rule:
Code Block |
---|
imgCard(Image, Html) :- imgCard(I), format(atom(Html), I, [Image]). |
In this rule, the Image
variable is the input and holds the URL of the image, whereas the Html
variable is the output HTML code for this card. In the body of the rule, the imgCard/1
is queried to retrieve the HTML code that we already specified. Then, the URL is placed at the location with `~a' in this HTML code (in the Image
variable), using the format/3
predicate.
We can now use the imgCard term to render each of the three images. For the complete display, we will combine them in the broader ‘card-deck’ option, like in the example HTML we got from bootstrap. We will do this using a self-defined Prolog term, with as input the text, button text and the three images, and as output the HTML code to render. Within this term, we will start specifying the main template, and then collect the images:
Code Block |
---|
myThreeImagesPage(Txt, Button, Image1, Image2, Image3, Html) :-
Template='<div class="card-deck">~a</div>',
imgCard(Image1,I), imgCard(Image2,I2), imgCard(Image1,I3),
atom_concat(I,I2,II), atom_concat(II,I3,III),
format(atom(Card), Template, [III]), html(Card, Html). |
Note that atom_concat is used to append the different HTML snippets for each of the images. Format is then used to plug in the HTML code where the `~a' has been specified in the Template variable (as was done for the imgCard).
The current term will display three given images on the screen. Of course, you need to make sure that the right image URLs can be displayed during the conversation, which you can do using Prolog facts in recipes.pl
.
Other Add-ons You Can Try
Info |
---|
Note that these add-ons use variable names, and functions from our version of the code, and may not align with the names you use. Make sure everything matches up properly! These are just examples, you can add anything your heart desires. |
Displaying Lists
Displaying lists on a screen is a practical feature when dealing with recipe selection and ingredients / utensils per recipe. Hereby some pointers on how to make this possible.
The first step is again to look for the proper template. The bootstrap list-groups template would make a good candidate here:
Code Block | ||
---|---|---|
| ||
<div class="card" style="width: 18rem;">
<ul class="list-group list-group-flush">
<li class="list-group-item">Cras justo odio</li>
<li class="list-group-item">Dapibus ac facilisis in</li>
<li class="list-group-item">Vestibulum at eros</li>
</ul>
</div> |
The goal here is to render the HTML code with a list of ingredients / recipes / … as input. We will again break it down, starting with specifying the list items:
Code Block |
---|
myListItem('<li class="list-group-item">~a</li>').
myListItem(Txt, Html) :- myListItem(T), format(atom(Html), T, [Txt]). |
The way this is defined will look familiar to you, but how are these list items combined based in a Prolog list? We will add another term for this, ‘myList/2', with as first variable the list of terms, and as second variable the HTML output:
Code Block |
---|
myList('<ul class="list-group list-group-flush">~a</ul>').
myList(List, Html) :- maplist(myListItem, List, Output), atomic_list_concat(Output, String),
myList(T), format(atom(Html), T, [String]). |
Code Block |
---|
myMaplist(_,[],[]).
myMaplist(P,[A|As],[B|Bs]) :- call(P,A,B),maplist(P,As,Bs). |
An alternative method is to use the built-in list group item in bootstrap, define each item then add them to a list.
Code Block |
---|
itemsList('<ul class="list-group">~a</ul>').
itemsList(List, Html) :-
maplist(listItem, List, Output), atomic_list_concat(Output, String),
itemsList(Template), applyTemplate(Template, String, Html).
listItem(Txt, Html) :- applyTemplate('<li class="list-group-item">~a</li>', Txt, Html). |
Making a Bullet Point List
Code Block | ||
---|---|---|
| ||
bulletItem(Item, Html) :- applyTemplate('<li>~a</li>', Item, Html).
bulletList(Items, Html) :-
maplist(bulletItem, Items, ItemsHtml),
atomic_list_concat(ItemsHtml, Bullets),
applyTemplate('<ul>~a</ul>', Bullets, Html). |
Making Buttons
Code Block | ||
---|---|---|
| ||
button('<button class="btn btn-light btn-lg m-3" style="font-size:1.5rem">~a</button>').
button(Content, Html) :- button(B), format(atom(Html), B, [Content]). |
...
Changing and receiving button events in a MARBEL agent
If you would inspect the html.pl
file and search for the button
predicate, you will find a button/2
predicate too. You will also see that both the button/2
and the button/3
predicates actually are defined in terms of a button/4
predicate button(Content, Class, DataValue, Html)
. This predicate allows to set the value send as a percept to the agent when the button is clicked. The button/3
definition by default sets this value equal to the Content
value displayed on the button. That, however, is not that useful if the content itself consists of HTML tags again. For example, button('<b>Start</b>', 'btn btn-secondary btn-lg', Html)
would display a bold text Start but would also send the tags as a percept of the form answer('<b>Start</b>')
when the button is clicked. To avoid that, we can use button('<b>Start</b>', 'btn btn-secondary btn-lg', 'start', Html)
which would send the more plain and easy to process answer('start')
to the MARBEL agent.
Besides the class
, style
, and data-value
attributes there are other global attributes in HTML. Because we think most of these have limited used we have not included these in the code provided to you. You are free, of course, to extend the Prolog code to be also able to use these other attributes.
Other tag examples: ul, img, div, span
ul tag
Displaying lists is a practical feature when you want to show a recipe and its associated ingredient list. The most basic way to do this is to use the unordered list ul
and list item li
tags. Here’s a simple example of what the HTML code would look like:
Code Block | ||
---|---|---|
| ||
<ul class="list-group">
<li class="list-group-item">1st item</li>
<li class="list-group-item">2nd item</li>
<li class="list-group-item">3rd item</li>
</ul> |
We have kept the class attribute simple but Bootstrap list groups provides several other values for the class attribute that can be used to change the basic behavior of the ul
tag. For example, use list-group-flush
for removing the borders and list-group-horizontal
for organizing the list items horizontally instead of vertically.
Even though ul
is a short tag we still have introduced a corresponding ul/3
predicate ul(Content, Class, Html)
because of the embedded content of list items that needs to be supplied and fit in between the start and end tags in this case. Using this predicate we can simply use ul(Content, 'list-group', Html)
for generating the unordered list example above. Clearly, a simple quotation approach will not work here as the list of items may easily get very long and unyieldy and upfront we may not even know how many items we need to add to the list (how many ingredients, for example, does a recipe have?). To facilitate the creation of the HTML list item elements, we have provided the predicate itemList(List, Html)
that generates the list item elements when provided with a list of the content for each item. For our example, itemList(['1st item', '2nd item', '3rd item'], Html)
will generate what we need as content for the ul
element.
The final step that remains is to piece the two things together to generate the complete HTML code for our example. For generating HTML code, we will use the fact that Prolog evaluates subqueries in a query in the order they appear. As a general rule of thumb, in this content, we should make sure to generate the HTML elements we need later first. That is, because we need the list item elements as content to complete the unordered list code, we need to create this code first. In other words, we will use the output argument of itemList/2
as the input argument for the content of ul/3
. By piecing the separate queries we defined for the list itself and for the list items together and renaming the appropriate argument variables, we get what we need:
Code Block |
---|
itemList(['1st item', '2nd item', '3rd item'], ListItems), ul(ListItems, 'list-group', Html) |
This combined query will generate our example HTML code and return it as value in the Html
variable.
Img tag
Div tag
Code Block |
---|
% card text
txtHtml('<div class="card-body"><p class="card-text">~a</p></div>').
txtHtml(Txt, Html) :- txtHtml(Template), applyTemplate(Template, Txt, Html). |
Span tag
'<span style="font-family:Roboto;">',
Check out the html.pl
file for more predefined Prolog predicates for HTML tags that you can use.
Generating an HTML webpage
By means of these and generate with the rule. The HTML code is represented in Bootstrap format, which is also clearly illustrated by examples below or on Bootstrap's documentation website (Bootstrap Documentation).
Note |
---|
Prolog Advice: To manipulate strings and atoms in Prolog it is useful to look at documentation of the following built-in functions: atomic_list_concat, atom_concat, string_concat, append, and maplist here: https://www.swi-prolog.org/. The predicate applyTemplate is a defined predicate that will be explained below. |
Prolog rules are used to add a condition to a webpage (i.e. webpage X is shown when Clause Y is true).
You add a condition that checks for a particular pattern ID to a Prolog rule in the html.pl
file. Other simple conditions that you can add involve counting the number of recipes that are still available while in the recipe selection phase, etc. The general form of the page lay-out rules would be something like:
Code Block |
---|
myPage(Txt, Button, Html) :-
<HERE YOUR CONDITION FOR SHOWING THIS PAGE>,
% e.g. while recipe is being selected (check for e.g. currently active top level pattern id), we'll display recipe features
% below code that specifies the page layout for this context
applyTemplate('<div class="card mx-auto" style="width:67vw">~a</div>, Text, Html),
…
. |
There are still many options to vary. For example, you can choose the parameters for the myPage
predicate that fit your approach best.
Example Prolog Rule for a Page
Code Block | ||
---|---|---|
| ||
page(c10, _, Html) :-
% Condition for when to show this page
CONDITION X,
% Constructing HTML page
atomic_list_concat(['<div class="alert alert-light"><center></br><h1>Hello!</br></br>','I am ~a</h1></br></br></center></div>'], Template),
% Get the bot's name if it has one; other call it 'your assistant'
(agentName(Name) -> N = Name ; N = 'your recipe selection assistant'), applyTemplate(Template, N, Body),
% Create the HTML page
html(Body, Html). |
A Quick Bootstrap Conceptual Walkthrough
Bootstrap is a useful framework for working out a display, but may not be so intuitive in combination with Prolog. We provide a short walkthrough here to explain things for you.
Let’s consider you would like to display a certain lay-out with three images side-by-side. The first step you would take is to check whether such a lay-out is specified in Bootstrap. ‘Card decks’ might be a nice option, so we first look into the code that is involved:
Code Block |
---|
<div class="card-deck">
<div class="card">
<img class="card-img-top" src=".../100px200/" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a longer card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
<div class="card">
<img class="card-img-top" src=".../100px200/" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This card has supporting text below as a natural lead-in to additional content.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
<div class="card">
<img class="card-img-top" src=".../100px200/" alt="Card image cap">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This card has even longer content than the first to show that equal height action.</p>
<p class="card-text"><small class="text-muted">Last updated 3 mins ago</small></p>
</div>
</div>
</div> |
What we want to achieve, is to render a page with the HTML code, but with a dynamic input of images. The trick is to break this down into fragments. First, we want to render a given image. In the HTML, we see that each of the three images are defined as separate cards. We will now use Prolog to generate the HTML for such a card, using a given picture URL. The aim is to connect in a single string the static HTML code and the image URL (which differs according to the context of the conversation). Let’s work with the good old Pasta Aglio URL for this example. We will define a predicate ‘imgCard’, with an arity of 1 and an arity of 2. We will give it once as a fact, specifying the HTML code:
Code Block |
---|
imgCard('<div class="card"><img class="card-img-top" src="~a" alt="Card image cap"></div>'). |
Note that the HTML is copied, with a replacement of the contents of ‘src’, in the form of ‘~a’ which is a placeholder for the image URL. This is needed in order to dynamically add this information. We will do this with the following rule:
Code Block |
---|
imgCard(Image, Html) :- imgCard(I), format(atom(Html), I, [Image]). |
In this rule, the Image
variable is the input and holds the URL of the image, whereas the Html
variable is the output HTML code for this card. In the body of the rule, the imgCard/1
is queried to retrieve the HTML code that we already specified. Then, the URL is placed at the location with `~a' in this HTML code (in the Image
variable), using the format/3
predicate.
We can now use the imgCard term to render each of the three images. For the complete display, we will combine them in the broader ‘card-deck’ option, like in the example HTML we got from bootstrap. We will do this using a self-defined Prolog term, with as input the text, button text and the three images, and as output the HTML code to render. Within this term, we will start specifying the main template, and then collect the images:
Code Block |
---|
myThreeImagesPage(Txt, Button, Image1, Image2, Image3, Html) :-
Template='<div class="card-deck">~a</div>',
imgCard(Image1,I), imgCard(Image2,I2), imgCard(Image1,I3),
atom_concat(I,I2,II), atom_concat(II,I3,III),
format(atom(Card), Template, [III]), html(Card, Html). |
Note that atom_concat is used to append the different HTML snippets for each of the images. Format is then used to plug in the HTML code where the `~a' has been specified in the Template variable (as was done for the imgCard).
The current term will display three given images on the screen. Of course, you need to make sure that the right image URLs can be displayed during the conversation, which you can do using Prolog facts in recipes.pl
.
Other Add-ons You Can Try
Info |
---|
Note that these add-ons use variable names, and functions from our version of the code, and may not align with the names you use. Make sure everything matches up properly! These are just examples, you can add anything your heart desires. |
Adding a chatbox to your page layout for testing purposes
The speech interface is fitting to the context of a user in a kitchen, but not always feasible for testing. It may take additional time for the system to process speech input, you may be in a noisy environment, and there may be speech recognition failures. Hereby a pointer to easily enable a textbox to be rendered for input.
In the ‘htmlhtml.
pl’ pl
file, where you format the pages to render during the conversation, for each of the pages that you may display at any point in the conversation add the following code:
Code Block |
---|
A chatbox can be added for using text instead of speech for input by adding
<div class="text-center"><p class="chatbox mx-auto"></p></div>, for example,
to the footer. |
Understanding the code in html.pl
applyTemplate('Template String with ~a where you want to insert atom', Atom to Insert, Return Variable).