Skip to content

Commit 510db31

Browse files
committed
update storyteller UI
1 parent 264d2ea commit 510db31

File tree

4 files changed

+139
-26
lines changed

4 files changed

+139
-26
lines changed

examples/bedtime-story-teller/assets/app.js

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,34 @@
44

55
const socket = io(`http://${window.location.host}`);
66

7+
let generateStoryButtonOriginalHTML = ''; // To store the original content of the generate story button
78

89

910
function initSocketIO() {
11+
socket.on('prompt', (data) => {
12+
const promptContainer = document.getElementById('prompt-container');
13+
const promptDisplay = document.getElementById('prompt-display');
14+
promptDisplay.innerHTML = data;
15+
promptContainer.style.display = 'flex';
16+
});
17+
1018
socket.on('response', (data) => {
1119
document.getElementById('story-container').style.display = 'flex';
1220
const storyResponse = document.getElementById('story-response');
13-
storyResponse.textContent = data;
14-
document.getElementById('loading-spinner').style.display = 'none';
21+
storyResponse.textContent += data;
22+
document.getElementById('loading-spinner').style.display = 'none'; // Hide the general loading spinner
1523
const clearStoryButton = document.getElementById('clear-story-button');
1624
clearStoryButton.style.display = 'block';
1725
clearStoryButton.disabled = false;
1826
});
27+
28+
socket.on('stream_end', () => {
29+
const generateStoryButton = document.querySelector('.generate-story-button');
30+
if (generateStoryButton) {
31+
generateStoryButton.disabled = false;
32+
generateStoryButton.innerHTML = generateStoryButtonOriginalHTML; // Restore original content
33+
}
34+
});
1935
}
2036

2137
function unlockAndOpenNext(currentContainer) {
@@ -168,9 +184,19 @@ function generateStory(data) {
168184
document.querySelector('.story-output-placeholder').style.display = 'none';
169185
const responseArea = document.getElementById('story-response-area');
170186
responseArea.style.display = 'flex';
187+
document.getElementById('prompt-container').style.display = 'none';
188+
document.getElementById('prompt-display').textContent = '';
171189
document.getElementById('story-container').style.display = 'none';
172-
document.getElementById('loading-spinner').style.display = 'block';
173190
document.getElementById('story-response').textContent = '';
191+
document.getElementById('loading-spinner').style.display = 'none'; // Hide the general loading spinner
192+
193+
const generateStoryButton = document.querySelector('.generate-story-button');
194+
if (generateStoryButton) {
195+
generateStoryButton.disabled = true;
196+
// Append the spinner instead of replacing innerHTML
197+
generateStoryButton.innerHTML += '<div class="button-spinner spinner"></div>';
198+
}
199+
174200
document.getElementById('clear-story-button').style.display = 'none';
175201
socket.emit('generate_story', data);
176202
}
@@ -223,10 +249,12 @@ function resetStoryView() {
223249
}
224250
}
225251

226-
// Hide "Generate story" button
252+
// Restore "Generate story" button to original state
227253
const generateStoryButton = document.querySelector('.generate-story-button');
228254
if (generateStoryButton) {
229-
generateStoryButton.style.display = 'none';
255+
generateStoryButton.style.display = 'none'; // Keep hidden if no chars, will be set to flex by checkCharactersAndUnlockNext
256+
generateStoryButton.disabled = false;
257+
generateStoryButton.innerHTML = generateStoryButtonOriginalHTML;
230258
}
231259

232260
// Reset parameter containers state
@@ -250,6 +278,11 @@ function resetStoryView() {
250278
document.addEventListener('DOMContentLoaded', () => {
251279
initSocketIO();
252280

281+
const generateStoryButton = document.querySelector('.generate-story-button');
282+
if (generateStoryButton) {
283+
generateStoryButtonOriginalHTML = generateStoryButton.innerHTML; // Store original content
284+
}
285+
253286
const parameterContainers = document.querySelectorAll('.parameter-container');
254287

255288
parameterContainers.forEach((container, index) => {

examples/bedtime-story-teller/assets/index.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ <h3 class="parameter-title">Other</h3>
175175
</div>
176176
<div id="story-response-area" style="display: none;">
177177
<div id="loading-spinner" class="spinner" style="display: none;"></div>
178+
<div class="response-container" id="prompt-container" style="display: none;">
179+
<h3 class="response-title">Prompt</h3>
180+
<div id="prompt-display" class="response-content"></div>
181+
</div>
178182
<div class="response-container" id="story-container" style="display: none;">
179183
<div class="response-header">
180184
<h3 class="response-title">Story</h3>

examples/bedtime-story-teller/assets/style.css

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,8 @@ body {
144144
.parameter-header {
145145
display: flex;
146146
align-items: center;
147-
}
147+
gap: 8px;
148148

149-
.parameter-header .parameter-title {
150-
margin-right: 8px; /* Gap between title and optional text/selected value */
151149
}
152150

153151
.parameter-header .arrow-icon {
@@ -224,9 +222,10 @@ body {
224222
border: 1px solid #C9D2D2;
225223
border-radius: 4px;
226224
background: #FFF;
227-
margin: 4px;
225+
margin: 0px 4px 4px 0px;
228226
font-size: 12px;
229227
font-weight: 600;
228+
white-space: nowrap;
230229
}
231230

232231
.selection-pill img {
@@ -266,6 +265,8 @@ body {
266265
font-weight: 400;
267266
line-height: 160%;
268267
letter-spacing: 0.12px;
268+
align-items: center;
269+
gap: 4px;
269270
}
270271

271272
.paragraph-title {
@@ -322,6 +323,11 @@ body {
322323
width: fit-content; /* Make it not full width */
323324
}
324325

326+
.generate-story-button:disabled {
327+
opacity: 0.6;
328+
cursor: not-allowed;
329+
}
330+
325331
.story-output-placeholder {
326332
display: flex;
327333
flex-direction: column;
@@ -615,11 +621,22 @@ body {
615621
overflow-y: auto;
616622
}
617623

624+
#prompt-display strong {
625+
color: #008184;
626+
font-weight: bold;
627+
}
628+
618629
.response-content strong {
619630
color: #25C2C7; /* Highlight color for parameters */
620631
}
621632

633+
.story-type-paragraph {
634+
margin-bottom: 8px;
635+
}
622636

637+
.response-title {
638+
margin-bottom: 8px;
639+
}
623640

624641
#story-container .response-title,
625642
#story-container .response-content {
@@ -637,6 +654,13 @@ body {
637654
display: inline-block;
638655
vertical-align: middle;
639656
}
657+
658+
.button-spinner.spinner {
659+
width: 20px;
660+
height: 20px;
661+
border-width: 2px; /* Smaller border for smaller spinner */
662+
margin: 0; /* Remove margin-left if inside button */
663+
}
640664
@keyframes spin {
641665
0% { transform: rotate(0deg); }
642666
100% { transform: rotate(360deg); }
@@ -650,14 +674,54 @@ body {
650674

651675
@media (max-width: 1024px) {
652676
.main-content {
653-
flex-direction: column;
654-
align-items: center;
655-
gap: 24px;
677+
grid-template-columns: 1fr; /* Switch to a single column layout */
678+
gap: 24px; /* Adjust the gap for the new single-column layout */
656679
}
657680

658-
.container {
659-
max-width: 800px;
660-
width: 90%;
681+
/* Reorder grid items for single-column layout */
682+
.main-content > .column-title:nth-child(1) {
683+
grid-row: 1; /* Parameters title */
684+
}
685+
686+
.main-content > .container { /* Parameters content */
687+
688+
grid-row: 2; /* Parameters content */
689+
690+
grid-column: 1;
691+
692+
max-width: none;
693+
694+
width: 100%;
695+
696+
padding: 0; /* Remove padding for alignment */
697+
698+
}
699+
700+
701+
702+
.your-story-header {
703+
704+
grid-row: 3; /* Story title */
705+
706+
grid-column: 1;
707+
708+
}
709+
710+
711+
712+
.story-output-container {
713+
714+
grid-row: 4; /* Story content */
715+
716+
grid-column: 1;
717+
718+
padding: 0; /* Remove padding for alignment */
719+
720+
}
721+
722+
/* Hide the empty div that created the column gap */
723+
.main-content > div:nth-child(2) {
724+
display: none;
661725
}
662726
}
663727

examples/bedtime-story-teller/python/main.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# SPDX-License-Identifier: MPL-2.0
44

55
import os
6+
import re
67
from arduino.app_bricks.cloud_llm import CloudLLM, CloudModel
78
from arduino.app_bricks.web_ui import WebUI
89
from arduino.app_utils import App
@@ -37,29 +38,40 @@ def generate_story(_, data):
3738
helper = find_character_by_role(characters, 'positive-helper')
3839
antagonist = find_character_by_role(characters, 'antagonist')
3940

40-
prompt = f"As a parent who loves to read bedtime stories to my {age} year old child, I need a delightful and age-appropriate story."
41+
# Create a prompt with HTML for display
42+
prompt_for_display = f"As a parent who loves to read bedtime stories to my <strong>{age}</strong> year old child, I need a delightful and age-appropriate story."
4143
if protagonist:
42-
prompt += f" about an {protagonist['description']}, {protagonist['name']}"
44+
prompt_for_display += f" about an <strong>{protagonist['description']}</strong>, <strong>{protagonist['name']}</strong>"
4345
else:
44-
prompt += " about a character"
46+
prompt_for_display += " about <strong>a character</strong>"
4547

4648
if helper:
47-
prompt += f" accompanied by his {helper['description']} helper {helper['name']}"
49+
prompt_for_display += f" accompanied by his <strong>{helper['description']}</strong> helper <strong>{helper['name']}</strong>"
4850
else:
49-
prompt += " accompanied by a friend"
50-
51+
prompt_for_display += " accompanied by <strong>a friend</strong>"
52+
5153
if antagonist:
52-
prompt += f" who will have to face the {antagonist['description']} antagonist {antagonist['name']}"
54+
prompt_for_display += f" who will have to face the <strong>{antagonist['description']}</strong> antagonist <strong>{antagonist['name']}</strong>"
5355
else:
54-
prompt += " who will have to face a villain"
56+
prompt_for_display += " who will have to face <strong>a villain</strong>"
5557

56-
prompt += f". The story type is {theme}. The tone should be {tone}. The format should be a narrative-style story with a clear beginning, middle, and end, allowing for a smooth and engaging reading experience. The objective is to entertain and soothe the child before bedtime. Provide a brief introduction to set the scene and introduce the main character. The scope should revolve around the topic: managing emotions and conflicts. The length should be approximately {duration}. Please ensure the story has a {narrative_structure} narrative structure, leaving the child with a sense of {ending_type}. The language should be easy to understand and suitable for my child's age comprehension."
58+
prompt_for_display += f". The story type is <strong>{theme}</strong>. The tone should be <strong>{tone}</strong>. The format should be a narrative-style story with a clear beginning, middle, and end, allowing for a smooth and engaging reading experience. The objective is to entertain and soothe the child before bedtime. Provide a brief introduction to set the scene and introduce the main character. The scope should revolve around the topic: managing emotions and conflicts. The length should be approximately <strong>{duration}</strong>. Please ensure the story has a <strong>{narrative_structure}</strong> narrative structure, leaving the child with a sense of <strong>{ending_type}</strong>. The language should be easy to understand and suitable for my child's age comprehension."
5759
if other:
58-
prompt += f"\n\nOther on optional stuff for the story: {other}"
60+
prompt_for_display += f"\n\nOther on optional stuff for the story: <strong>{other}</strong>"
61+
62+
# Create a plain text prompt for the LLM by stripping HTML tags
63+
prompt_for_llm = re.sub('<[^>]*>', '', prompt_for_display)
5964

60-
for resp in llm.chat_stream(prompt):
65+
# Send the display prompt to the UI
66+
ui.send_message("prompt", prompt_for_display)
67+
68+
# Use the plain text prompt for the LLM and stream the response
69+
for resp in llm.chat_stream(prompt_for_llm):
6170
ui.send_message("response", resp)
6271

72+
# Signal the end of the stream
73+
ui.send_message("stream_end", {})
74+
6375
ui.on_message("generate_story", generate_story)
6476

6577
App.run()

0 commit comments

Comments
 (0)