Spaces:
Running
Running
<html> | |
<head> | |
<title>SocialAI School Demo</title> | |
<style> | |
form { | |
display: inline-block; | |
display: inline-block; | |
margin-left: 2px; | |
margin-right: 2px; | |
margin-bottom: 2px; | |
margin-top: 2px; | |
} | |
.controls-container { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; /* aligns children to the start */ | |
} | |
.form-container { | |
display: flex; | |
align-items: center; | |
margin-bottom: 10px; /* provides spacing between controls */ | |
} | |
.form-label { | |
width: 200px; /* adjust this value as needed */ | |
text-align: right; | |
margin-right: 10px; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: flex-start; /* Align to the top/start */ | |
height: 100vh; | |
margin: 0; /* Remove default margin */ | |
padding: 10px; /* Optional: Add some padding at the top for spacing */ | |
} | |
.btn-container { | |
display: flex; | |
flex-direction: column; | |
margin-bottom: 20px; | |
} | |
.action-row { | |
display: flex; | |
flex-direction: row; | |
gap: 10px; | |
margin-bottom: 10px; /* Adds a space between the two rows */ | |
} | |
.tree-container { | |
width: 60%; | |
min-width: 1000px; | |
max-height: 600px; | |
margin-left: 5px; | |
margin-right: 5px; | |
margin-bottom: 5px; | |
margin-top: 5px; | |
padding-top: 10px; | |
padding-left: 30px; | |
padding-right: 30px; | |
border: 5px solid #ccc; /* Optional: just to visualize the container's boundaries */ | |
border-radius: 10px; /* uniform roundness */ | |
box-shadow: 3px 3px 5px #888888; /* horizontal offset | vertical offset | blur radius | color */ | |
} | |
.env-container { | |
width: 60%; /* Adjust to desired width */ | |
min-width: 1000px; /* Adjust to desired width */ | |
margin-left: 5px; | |
margin-right: 5px; | |
margin-bottom: 5px; | |
margin-top: 5px; | |
padding-top: 2px; | |
padding-left: 30px; | |
padding-right: 30px; | |
padding-bottom: 30px; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
border: 3px solid #ccc; /* Optional: just to visualize the container's boundaries */ | |
border-radius: 10px; /* uniform roundness */ | |
box-shadow: 3px 3px 5px #888888; /* horizontal offset | vertical offset | blur radius | color */ | |
} | |
.env-flex { | |
display: flex; | |
align-items: flex-start; /* to align items to the top */ | |
} | |
img { | |
margin-left: 5px; | |
margin-right: 5px; | |
margin-bottom: 5px; | |
margin-top: 5px; | |
} | |
#envImage { | |
width: 100%; /* Make image responsive */ | |
min-width: 500px; | |
height: auto; /* Maintain aspect ratio */ | |
display: block; /* Remove default image inline behaviors */ | |
margin-left: auto; /* Center the image horizontally if needed */ | |
margin-right: auto; /* Center the image horizontally if needed */ | |
} | |
.bubble { | |
margin-left: 20px; /* space between the image and the bubble */ | |
position: relative; | |
background-color: #f7f8f9; | |
border-radius: 10px; | |
padding: 10px; | |
width: max-content; | |
width: 100%; /* Make image responsive */ | |
height: auto; /* Maintain aspect ratio */ | |
min-width: 500px; | |
margin-bottom: 20px; | |
} | |
/* The switch container */ | |
.switch { | |
position: relative; | |
display: inline-block; | |
width: 60px; | |
height: 34px; | |
} | |
/* Hide default HTML checkbox */ | |
.switch input { | |
opacity: 0; | |
width: 0; | |
height: 0; | |
} | |
/* The slider (the on/off part) */ | |
.slider { | |
position: absolute; | |
cursor: pointer; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
background-color: #ccc; | |
transition: 0.4s; | |
} | |
.slider:before { | |
position: absolute; | |
content: ""; | |
height: 26px; | |
width: 26px; | |
left: 4px; | |
bottom: 4px; | |
background-color: white; | |
transition: 0.4s; | |
} | |
/* When input is checked, slide to the right */ | |
input:checked + .slider { | |
background-color: #2196F3; | |
} | |
input:checked + .slider:before { | |
transform: translateX(26px); | |
} | |
/* Round sliders */ | |
.slider.round { | |
border-radius: 34px; | |
} | |
.slider.round:before { | |
border-radius: 50%; | |
} | |
</style> | |
<script> | |
function performAction(actionName) { | |
let bodyData = `action=${actionName}`; | |
// If the action is "speak", fetch the selected template and word | |
if (actionName === "speak") { | |
const template = document.getElementById('actionTemplate').value; | |
const word = document.getElementById('actionWord').value; | |
bodyData += `&template=${template}&word=${word}`; | |
} | |
fetch('/perform_action', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded', | |
}, | |
body: bodyData | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
document.getElementById('envImage').src = `data:image/jpeg;base64,${data.image_data}`; | |
if (actionName === "done") { | |
// Save the scroll position in sessionStorage before reloading | |
sessionStorage.setItem('scrollPosition', window.scrollY); | |
console.log(window.scrollY); | |
// Function to reload the page with updated URL parameter | |
function reloadPage() { | |
let currentUrl = new URL(window.location.href); | |
currentUrl.searchParams.set('_', new Date().getTime()); | |
window.location.href = currentUrl.href; | |
} | |
// Call the function to reload the page | |
reloadPage(); | |
<!-- const svgTree = document.getElementById('svgTree');--> | |
<!-- svgTree.data = `./static/current_tree.svg?_=${data.timestamp}`;--> | |
<!-- // todo: update the dropdown lists for parameters--> | |
} | |
// Add this to handle the caretaker's utterance | |
let bubble = document.getElementById('caretakerBubble'); | |
// Check for success | |
if (data.done) { | |
if (data.success) { | |
bubble.innerHTML = '<br><div style="text-align: center; color: green;">SUCCESS!</div><br>' + | |
'<div style="text-align: center; color: gray; font-style: italic; font-size: smaller;">(press Enter to continue)</div><br>'; | |
bubble.style.display = 'block'; | |
} else { | |
bubble.innerHTML = '<br><div style="text-align: center; color: red;">FAILURE!</div><br>' + | |
'<div style="text-align: center; color: gray; font-style: italic; font-size: smaller;">(press Enter to continue)</div><br>'; | |
bubble.style.display = 'block'; | |
} | |
} else if (data.bubble_text) { | |
let formattedText = data.bubble_text.replace(/\n/g, '<br>'); | |
bubble.innerHTML = formattedText; | |
bubble.style.display = 'block'; | |
} else { | |
bubble.style.display = 'none'; | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
}); | |
} | |
document.addEventListener("DOMContentLoaded", function() { | |
document.body.addEventListener("keydown", function(event) { | |
if (event.key === "ArrowLeft") { | |
event.preventDefault(); | |
performAction('left'); | |
} else if (event.key === "ArrowUp") { | |
event.preventDefault(); | |
performAction('forward'); | |
} else if (event.key === "ArrowRight") { | |
event.preventDefault(); | |
performAction('right'); | |
} else if (event.key === "s") { | |
event.preventDefault(); | |
performAction('speak'); | |
} else if (event.key === "Spacebar" || event.key === " ") { | |
event.preventDefault(); | |
performAction('toggle'); | |
} else if (event.key === "Shift") { // You had two conditions for Shift, so I've merged them. | |
event.preventDefault(); | |
performAction('noop'); | |
} else if (event.key === "Enter") { | |
event.preventDefault(); | |
performAction('done'); | |
} | |
}); | |
}); | |
// handle the change event of the mask unobserved slider change | |
function updateMaskUnobserved() { | |
const maskUnobservedValue = document.querySelector('input[name="mask_unobserved"]').checked; | |
fetch('/set_mask_unobserved', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded', | |
}, | |
body: `mask_unobserved=${maskUnobservedValue}` | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Update the image src with the new image data | |
document.getElementById('envImage').src = `data:image/jpeg;base64,${data.image_data}`; | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
}); | |
} | |
// handle the change event of the textual observations slider change | |
function updateTextualObservations() { | |
const textualObservationsValue = document.querySelector('input[name="textual_observations"]').checked; | |
fetch('/set_textual_observations', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded', | |
}, | |
body: `textual_observations=${textualObservationsValue}` | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
// Add this to handle the caretaker's utterance | |
let bubble = document.getElementById('caretakerBubble'); | |
// Check for success | |
if (data.done) { | |
if (data.success) { | |
bubble.innerHTML = '<br><div style="text-align: center; color: green;">SUCCESS!</div><br>' + | |
'<div style="text-align: center; color: gray; font-style: italic; font-size: smaller;">(press Enter to continue)</div><br>'; | |
bubble.style.display = 'block'; | |
} else { | |
bubble.innerHTML = '<br><div style="text-align: center; color: red;">FAILURE!</div><br>' + | |
'<div style="text-align: center; color: gray; font-style: italic; font-size: smaller;">(press Enter to continue)</div><br>'; | |
bubble.style.display = 'block'; | |
} | |
} else if (data.bubble_text) { | |
let formattedText = data.bubble_text.replace(/\n/g, '<br>'); | |
bubble.innerHTML = formattedText; | |
bubble.style.display = 'block'; | |
} else { | |
bubble.style.display = 'none'; | |
} | |
}) | |
.catch(error => { | |
console.error('Error:', error); | |
}); | |
} | |
function setEnvParams() { | |
// Collect data from dropdowns | |
var data = {}; | |
{% for key in parameter_options.keys() %} | |
data['{{ key.id }}'] = document.getElementById('{{ key.id }}').value; | |
{% endfor %} | |
// Send data to Flask backend | |
fetch('/set_env_params', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify(data), | |
}) | |
.then(response => { | |
if(response.ok) { | |
// If the request was successful, reload the page without cache | |
let currentUrl = new URL(window.location.href); | |
currentUrl.searchParams.set('_', new Date().getTime()); | |
window.location.href = currentUrl.href; | |
} else { | |
// Handle errors | |
console.error('Error in setEnvParams:', response); | |
} | |
}) | |
.catch((error) => { | |
console.error('Error:', error); | |
}); | |
} | |
function restoreScrollPosition() { | |
let savedScrollPosition = parseInt(sessionStorage.getItem('scrollPosition')); | |
if (!isNaN(savedScrollPosition)) { | |
window.scrollTo(0, savedScrollPosition); | |
sessionStorage.removeItem('scrollPosition'); | |
} | |
} | |
// Attach the restoreScrollPosition function to the appropriate event | |
window.addEventListener('load', restoreScrollPosition); | |
</script> | |
</head> | |
<body> | |
<div class="controls-container"> | |
<div class="form-container"> | |
<span class="form-label">Select Environment:</span> | |
<form method="post" action="/set_env" id="envForm"> | |
<select name="env_label" onchange="document.getElementById('envForm').submit();"> | |
{% for env in available_env_labels %} | |
<option value="{{ env }}" {% if env == current_env_label %}selected{% endif %} {% if env.startswith("-")%}disabled{% endif %}>{{ env }}</option> | |
{% endfor %} | |
</select> | |
</form> | |
</div> | |
<div class="form-container"> | |
<span class="form-label">Mask unobserved cells:</span> | |
<label class="switch"> | |
<input type="checkbox" name="mask_unobserved" value="true" onchange="updateMaskUnobserved()" {% if mask_unobserved %}checked{% endif %}> | |
<span class="slider round"></span> | |
</label> | |
</div> | |
<div class="form-container"> | |
<span class="form-label">Textual observation:</span> | |
<label class="switch"> | |
<input type="checkbox" name="textual_observations" value="true" onchange="updateTextualObservations()" {% if textual_observations %}checked{% endif %}> | |
<span class="slider round"></span> | |
</label> | |
</div> | |
</div> | |
<p>This is the sampling tree. The current sampled parameters are highlighted blue</p> | |
<div class="tree-container" > | |
<div class="form-container"> | |
<span class="form-label">Select Parameters:</span> | |
<form method="post" id="paramsEnvForm"> | |
{% set ns = namespace(lvl=1) %} | |
{% for key, options in parameter_options.items() %} | |
{% if key.level != ns.lvl %} | |
{% if ns.lvl >= 0 %}<hr>{% endif %} | |
{% set ns.lvl = key.level %} | |
{% endif %} | |
<label>{{ key.label }}</label> | |
<select name="{{ key }}" id="{{ key.id }}" onchange="setEnvParams();"> | |
{% for option in options %} | |
<option value="{{ option.id }}" {% if current_parameters[key].id == option.id %}selected{% endif %} > | |
{{ option.label }} | |
</option> | |
{% endfor %} | |
</select> | |
{% endfor %} | |
</form> | |
</div> | |
<object id="svgTree" type="image/svg+xml" data="{{ url_for('static', filename='current_tree.svg', _=timestamp) }}" width="95%" height="70%">Your browser does not support SVG</object> | |
</div> | |
<p>This is the environment.</p> | |
<div class="env-container"> | |
<p>You can control the agent using the following actions:</p> | |
<div class="btn-container"> | |
<div class="action-row">Primitive actions: | |
<button onclick="performAction('left')">Left [←]</button> | |
<button onclick="performAction('forward')">Forward [↑]</button> | |
<button onclick="performAction('right')">Right [→]</button> | |
<button onclick="performAction('toggle')">Toggle [Spacebar]</button> | |
<button onclick="performAction('noop')">No-op [Shift]</button> | |
<button onclick="performAction('done')">Done [Enter]</button> | |
</div> | |
<div class="action-row"> | |
Speaking actions: | |
<select id="actionTemplate"> | |
{% for templ in grammar_templates %} | |
<option value="{{ templ }}" {{ 'selected' if templ == "Help" else '' }}>{{ templ }}</option> | |
{% endfor %} | |
</select> | |
<select id="actionWord"> | |
{% for word in grammar_words %} | |
<option value="{{ word }}" {{ 'selected' if word == "please" else '' }}>{{ word }}</option> | |
{% endfor %} | |
</select> | |
<button onclick="performAction('speak')">Speak [s]</button> | |
</div> | |
</div> | |
<div class="env-flex" > | |
<img id="envImage" src="data:image/jpeg;base64,{{ image_data }}" alt="State of the environment"> | |
<div id="caretakerBubble" class="bubble"> | |
{{ bubble_text }} | |
</div> | |
</div> | |
</div> | |
</body> | |
</html> | |