Skip to content

Commit 1c78fe1

Browse files
authored
Added "CustomExternalUI" Sample with instructions to change the UI for a Copilot agent on a website (#269)
* Create README.MD * Added assets folder * Added src folder * Add files via upload * Delete CustomExternalUI/src/test.MD * Add files via upload * Update README.MD * Update README.MD * Added html file * Delete CustomExternalUI/assets/tes.MD * Delete CustomExternalUI/src/chatbot_ui_comparison.png * Update README.MD
1 parent 8c13af1 commit 1c78fe1

File tree

3 files changed

+309
-0
lines changed

3 files changed

+309
-0
lines changed

CustomExternalUI/README.MD

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Custom External UI
2+
3+
The provided html-file can be integrated in your external website or published in a Power Pages website.
4+
The solution uses the Customization Options from the Azure AI Bot Services as well as some CSS to drastically change the look of your Copilot agent.
5+
6+
## Example
7+
8+
This is an example for a bot before and after using the provided sample
9+
10+
<img src="assets/chatbot_ui_comparison.png" alt="Custom chatbot UI compared to the standard UI">
11+
12+
## Instructions
13+
14+
Create your Copilot agent and publish it.
15+
16+
Download the file [CustomChatbotUI.html](src/CustomChatbotUI.html) and open it in a code editor.
17+
18+
Replace the following values with values of your choice.
19+
20+
| Old Value | Replace with |
21+
|-|-|
22+
| CustomTitle | The name of your bot |
23+
| CustomFont | Your font family (can provide backups like: Helvetica, Arial, ...) |
24+
| CustomAccentColor | Accent Color in Hex Code, pick a dark color with enough contrast to white (example: #000099) |
25+
| CustomFontSize | Your preferred font-size for the chat (example: 14px) |
26+
| CustomBotAvatar | Link to avatar image for bot (example: https://i.imgur.com/v0pQRJe.jpeg) |
27+
| CustomUserAvatar | Link to avatar image for user (example: https://i.imgur.com/Vd2rwWx.jpeg) |
28+
| YourTokenURL | Token Endpoint, can be obtained from the mobile Channel in Copilot Studio |
29+
30+
Please play around yourself! Feel free to change the size of your containers, colors and much more in the html!
31+
32+
Also take a look at the documentation for the Web Chat customization - there are more options available: https://learn.microsoft.com/en-us/azure/bot-service/bot-builder-webchat-customization?view=azure-bot-service-4.0
33+
34+
## Downsides
35+
36+
This is only configured for certain aspects of adaptive cards. Feel free to expand it and add it in the file.
37+
There is also no guarantee for this to work with older or future versions of adaptive cards.
281 KB
Loading
Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<title>Facial Palsy UK Virtual Assistant</title>
6+
<style>
7+
html, body {
8+
height: 100%;
9+
margin: 0;
10+
font-family: Roboto, sans-serif !important;
11+
background-color: #f4f6f9;
12+
/*font-size: 14px !important;*/
13+
}
14+
.chat-container {
15+
display: none;
16+
flex-direction: column;
17+
justify-content: center;
18+
align-items: center;
19+
position: fixed;
20+
bottom: 80px;
21+
right: 18px;
22+
width: 100%;
23+
max-width: 500px;
24+
min-height: 300px;
25+
height: 600px;
26+
max-height: 80%;
27+
z-index: 1000;
28+
}
29+
.chat-icon {
30+
position: fixed;
31+
bottom: 15px;
32+
right: 15px;
33+
width: 50px;
34+
height: 50px;
35+
background-color: #000099;
36+
border: 2px solid white;
37+
border-radius: 50%;
38+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
39+
display: flex;
40+
justify-content: center;
41+
align-items: center;
42+
cursor: pointer;
43+
z-index: 1001;
44+
animation: fadeIn 0.5s ease;
45+
}
46+
.chat-icon img {
47+
width: 30px;
48+
height: 30px;
49+
animation: fadeIn 0.5s ease;
50+
}
51+
.chat-icon.cancel-icon::before {
52+
content: "✖";
53+
font-size: 24px;
54+
color: white;
55+
animation: fadeIn 0.5s ease;
56+
}
57+
#banner h1 {
58+
font: Roboto;
59+
color: #ffffff;
60+
font-size: 24px;
61+
line-height: 28px;
62+
margin: 0;
63+
padding: 0 20px;
64+
transition: color 0.3s;
65+
}
66+
67+
#banner {
68+
align-items: center;
69+
background-color: #000099;
70+
display: flex;
71+
height: 70px;
72+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
73+
width: 100%;
74+
justify-content: center;
75+
border-radius: 10px 10px 0 0;
76+
border: 1px solid #e2e2e2;
77+
animation: fadeIn 0.5s ease;
78+
}
79+
#webchat {
80+
background-color: #ffffff;
81+
flex-grow: 1;
82+
overflow: hidden;
83+
width: 100%;
84+
height: 100%;
85+
box-sizing: border-box;
86+
padding: 20px;
87+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
88+
border-radius: 0 0 10px 10px;
89+
animation: fadeIn 0.5s ease;
90+
border: 1px solid #e2e2e2;
91+
}
92+
@keyframes fadeIn {
93+
from {
94+
opacity: 0;
95+
}
96+
to {
97+
opacity: 1;
98+
}
99+
}
100+
@media (max-width: 600px) {
101+
#banner {
102+
flex-direction: column;
103+
height: auto;
104+
padding: 10px;
105+
}
106+
h1 {
107+
padding: 10px 0;
108+
}
109+
}
110+
111+
/* Overriding Styles for adaptive cards */
112+
.ac-multichoiceInput {
113+
padding: 3px !important;
114+
border-radius: 4px;
115+
}
116+
117+
.ac-pushButton {
118+
color: rgb(0, 0, 153);
119+
border-color: rgb(0, 0, 153);
120+
font-weight: 300 !important;
121+
padding: 5px !important;
122+
border-radius: 4px;
123+
background-color: transparent !important;
124+
width: fit-content !important;
125+
max-width: 100% !important;
126+
}
127+
128+
.ac-pushButton:hover {
129+
background-color: rgb(0, 0, 153, 0.2) !important;
130+
cursor: pointer;
131+
}
132+
133+
.ac-pushButton[aria-pressed="true"] {
134+
background-color: rgb(0, 0, 153) !important;
135+
color: white;
136+
}
137+
138+
button[class*="suggested-action"] {
139+
padding: 5px !important;
140+
height: fit-content !important;
141+
border-radius: 4px !important;
142+
background-color: transparent !important;
143+
cursor: pointer;
144+
max-width: 100% !important;
145+
}
146+
147+
button[class*="suggested-action"]:hover {
148+
background-color: rgb(0, 0, 153, 0.2) !important;
149+
}
150+
151+
.webchat__bubble__content * {
152+
font-size: 14px !important;
153+
font-family: Roboto, sans-serif !important;
154+
}
155+
</style>
156+
</head>
157+
158+
<body>
159+
<div class="chat-container" id="chat-container">
160+
<div id="banner">
161+
<h1>Facial Palsy UK Virtual Assistant</h1>
162+
</div>
163+
<div id="webchat" role="main"></div>
164+
</div>
165+
<div class="chat-icon" id="chat-icon">
166+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-3 -3 30 30">
167+
<path d="M12,3C6.5,3 2,6.58 2,11C2.05,13.15 3.06,15.17 4.75,16.5C4.75,17.1 4.33,18.67 2,21C4.37,20.89 6.64,20 8.47,18.5C9.61,18.83 10.81,19 12,19C17.5,19 22,15.42 22,11C22,6.58 17.5,3 12,3M12,17C7.58,17 4,14.31 4,11C4,7.69 7.58,5 12,5C16.42,5 20,7.69 20,11C20,14.31 16.42,17 12,17Z" fill="white" />
168+
</svg>
169+
</div>
170+
<script crossorigin="anonymous" src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
171+
<script>
172+
document.getElementById('chat-icon').addEventListener('click', function () {
173+
const chatContainer = document.getElementById('chat-container');
174+
const chatIcon = document.getElementById('chat-icon');
175+
const isVisible = chatContainer.style.display === 'flex';
176+
chatContainer.style.display = isVisible ? 'none' : 'flex';
177+
if (isVisible) {
178+
chatIcon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="-3 -3 30 30"> <path d="M12,3C6.5,3 2,6.58 2,11C2.05,13.15 3.06,15.17 4.75,16.5C4.75,17.1 4.33,18.67 2,21C4.37,20.89 6.64,20 8.47,18.5C9.61,18.83 10.81,19 12,19C17.5,19 22,15.42 22,11C22,6.58 17.5,3 12,3M12,17C7.58,17 4,14.31 4,11C4,7.69 7.58,5 12,5C16.42,5 20,7.69 20,11C20,14.31 16.42,17 12,17Z" fill="white" /> </svg> ';
179+
chatIcon.classList.remove('cancel-icon');
180+
} else {
181+
chatIcon.innerHTML = '';
182+
chatIcon.classList.add('cancel-icon');
183+
}
184+
});
185+
186+
(async function () {
187+
const styleOptions = {
188+
accent: '#000099',
189+
botAvatarBackgroundColor: '#000000',
190+
botAvatarImage: 'https://i.imgur.com/v0pQRJe.jpeg',
191+
botAvatarInitials: ' ',
192+
userAvatarBackgroundColor: '#555555',
193+
userAvatarImage: 'https://i.imgur.com/Vd2rwWx.jpeg',
194+
userAvatarInitials: ' ',
195+
backgroundColor: '#f4f6f9',
196+
bubbleBackground: '#E2E2E2',
197+
bubbleTextColor: '#333333',
198+
bubbleFromUserBackground: '#E2E2E2',
199+
bubbleFromUserTextColor: '#333333',
200+
sendBoxBackground: '#F4F4F4',
201+
sendBoxTextColor: '#333333',
202+
sendBoxButtonColor: '#000000',
203+
avatarBorderRadius: '50%',
204+
avatarSize: 40,
205+
showAvatarInGroup: true,
206+
bubbleBorderRadius: '8px',
207+
bubbleFromUserBorderRadius: '8px',
208+
bubbleNubSize: '10px',
209+
bubbleFromUserNubSize: '10px',
210+
sendBoxButtonColorOnFocus: '#c4005d',
211+
sendBoxButtonColorOnHover: '#c4005d',
212+
connectivityIconPadding: '10px',
213+
connectivityMarginTopBottom: '10px',
214+
notificationText: '#4A4A4A',
215+
transcriptOverlayBackground: 'rgba(255, 255, 255, 0.8)',
216+
transcriptOverlayPadding: '20px',
217+
transcriptVerticalSpacer: '10px',
218+
hideUploadButton: true,
219+
};
220+
221+
const tokenEndpointURL = 'https://Defaultb62a9626ac0e43b7aee7c0d0a11492.90.environment.api.powerplatform.com/powervirtualagents/botsbyschema/crfad_facialPalsyCoPilot/directline/token?api-version=2022-03-01-preview';
222+
const locale = document.documentElement.lang || 'en';
223+
const apiVersion = new URL(tokenEndpointURL).searchParams.get('api-version');
224+
225+
const [directLineURL, token] = await Promise.all([
226+
fetch(new URL(`/powervirtualagents/regionalchannelsettings?api-version=${apiVersion}`, tokenEndpointURL))
227+
.then(response => {
228+
if (!response.ok) {
229+
throw new Error('Failed to retrieve regional channel settings.');
230+
}
231+
return response.json();
232+
})
233+
.then(({ channelUrlsById: { directline } }) => directline),
234+
fetch(tokenEndpointURL)
235+
.then(response => {
236+
if (!response.ok) {
237+
throw new Error('Failed to retrieve Direct Line token.');
238+
}
239+
return response.json();
240+
})
241+
.then(({ token }) => token)
242+
]);
243+
244+
const directLine = WebChat.createDirectLine({
245+
domain: new URL('v3/directline', directLineURL),
246+
token
247+
});
248+
249+
const subscription = directLine.connectionStatus$.subscribe({
250+
next(value) {
251+
if (value === 2) {
252+
directLine.postActivity({
253+
localTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
254+
locale,
255+
name: 'startConversation',
256+
type: 'event'
257+
}).subscribe();
258+
subscription.unsubscribe();
259+
}
260+
}
261+
});
262+
263+
WebChat.renderWebChat({
264+
directLine,
265+
locale,
266+
styleOptions
267+
}, document.getElementById('webchat'));
268+
})();
269+
</script>
270+
</body>
271+
272+
</html>

0 commit comments

Comments
 (0)