236 lines
11 KiB
HTML
236 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Rhai Script Playground</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/codemirror@5.65.2/lib/codemirror.css">
|
|
<link rel="stylesheet" href="style.css">
|
|
<style>
|
|
.btn-success { background-color: #20c997 !important; border-color: #20c997 !important; color: white !important; }
|
|
.btn-danger { background-color: #f87171 !important; border-color: #f87171 !important; color: white !important; }
|
|
.btn-warning { background-color: #ffc107 !important; border-color: #ffc107 !important; color: black !important; }
|
|
|
|
/* Toast styling */
|
|
.toast-container {
|
|
z-index: 1050;
|
|
}
|
|
|
|
.toast {
|
|
min-width: 300px;
|
|
opacity: 1 !important;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container-fluid">
|
|
<header class="py-3 mb-3 border-bottom">
|
|
<h1 class="text-center">Rhai Script Talk</h1>
|
|
</header>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6 pe-md-2">
|
|
<div class="card mb-3">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">Script Input</h5>
|
|
<div>
|
|
<select id="modelSelect" class="form-select form-select-sm">
|
|
<option value="calendar">Calendar Model</option>
|
|
<option value="governance">Governance Model</option>
|
|
</select>
|
|
</div>
|
|
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<textarea id="scriptInput"></textarea>
|
|
</div>
|
|
<div class="card-footer d-flex justify-content-between">
|
|
<button id="loadExampleBtn" class="btn btn-outline-secondary">Load Example</button>
|
|
<button id="runScriptBtn" class="btn btn-primary">Run Script</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 ps-md-2">
|
|
<div class="card mb-3">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">Output</h5>
|
|
<div class="d-flex align-items-center">
|
|
<div class="input-group me-2" style="width: 250px;">
|
|
<select id="listenerSelect" class="form-select form-select-sm">
|
|
<option value="">Custom URL...</option>
|
|
<option value="http://localhost:3000/governance">Governance Listener</option>
|
|
<option value="http://localhost:3000/finance">Finance Listener</option>
|
|
<option value="http://localhost:3000/calendar">Calendar Listener</option>
|
|
</select>
|
|
<input type="text" id="listenerUrl" class="form-control form-control-sm" placeholder="Listener URL" style="display: none;">
|
|
</div>
|
|
<button id="connectListenerBtn" class="btn btn-sm btn-outline-secondary d-flex align-items-center">
|
|
<span id="statusCircle" class="me-2" style="width: 8px; height: 8px; border-radius: 50%; background-color: #6c757d; display: inline-block;"></span>
|
|
<span id="connectBtnText">Connect</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<pre id="outputArea" class="p-3"></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Toast container for notifications -->
|
|
<div class="toast-container position-fixed bottom-0 end-0 p-3"></div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const listenerUrlInput = document.getElementById('listenerUrl');
|
|
const listenerSelect = document.getElementById('listenerSelect');
|
|
const connectListenerBtn = document.getElementById('connectListenerBtn');
|
|
const statusCircle = document.getElementById('statusCircle');
|
|
const toastContainer = document.querySelector('.toast-container');
|
|
|
|
// Handle listener selection dropdown
|
|
listenerSelect.addEventListener('change', () => {
|
|
const selectedValue = listenerSelect.value;
|
|
|
|
if (selectedValue === '') {
|
|
// Show custom URL input when "Custom URL..." is selected
|
|
listenerUrlInput.style.display = 'block';
|
|
listenerUrlInput.focus();
|
|
} else {
|
|
// Hide custom URL input when a predefined option is selected
|
|
listenerUrlInput.style.display = 'none';
|
|
// Clear any previous custom URL
|
|
listenerUrlInput.value = '';
|
|
}
|
|
});
|
|
|
|
// Function to show toast notifications
|
|
function showToast(message, type = 'error') {
|
|
const toastId = 'toast-' + Date.now();
|
|
const bgClass = type === 'error' ? 'bg-danger' : (type === 'warning' ? 'bg-warning' : 'bg-info');
|
|
const headerText = type === 'error' ? 'Error' : (type === 'warning' ? 'Warning' : 'Info');
|
|
|
|
// Create the toast element
|
|
const toastDiv = document.createElement('div');
|
|
toastDiv.className = 'toast mb-3';
|
|
toastDiv.id = toastId;
|
|
toastDiv.setAttribute('role', 'alert');
|
|
toastDiv.setAttribute('aria-live', 'assertive');
|
|
toastDiv.setAttribute('aria-atomic', 'true');
|
|
|
|
toastDiv.innerHTML = `
|
|
<div class="toast-header ${bgClass} text-white">
|
|
<strong class="me-auto">${headerText}</strong>
|
|
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
|
|
</div>
|
|
<div class="toast-body">
|
|
${message}
|
|
</div>
|
|
`;
|
|
|
|
// Add to container
|
|
toastContainer.appendChild(toastDiv);
|
|
|
|
// Initialize and show the toast
|
|
const toast = new bootstrap.Toast(toastDiv, {
|
|
autohide: true,
|
|
delay: 10000
|
|
});
|
|
toast.show();
|
|
|
|
// Remove the toast element after it's hidden
|
|
toastDiv.addEventListener('hidden.bs.toast', () => {
|
|
toastDiv.remove();
|
|
});
|
|
}
|
|
|
|
connectListenerBtn.addEventListener('click', async () => {
|
|
// Get URL from either dropdown or text input
|
|
let url = listenerSelect.value;
|
|
|
|
// If custom URL option is selected, use the text input value
|
|
if (url === '') {
|
|
url = listenerUrlInput.value.trim();
|
|
if (!url) {
|
|
showToast('Please enter a listener URL.');
|
|
return;
|
|
}
|
|
}
|
|
|
|
const connectBtnText = document.getElementById('connectBtnText');
|
|
const statusCircle = document.getElementById('statusCircle');
|
|
|
|
// Set to connecting state
|
|
connectBtnText.textContent = 'Connecting...';
|
|
connectListenerBtn.classList.remove('btn-outline-secondary', 'btn-success', 'btn-danger');
|
|
connectListenerBtn.classList.add('btn-warning');
|
|
statusCircle.style.backgroundColor = '#e6a800'; // Darker yellow for the circle
|
|
|
|
// Disable the button during connection attempts
|
|
connectListenerBtn.disabled = true;
|
|
|
|
// Function to attempt a ping
|
|
const attemptPing = async () => {
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: 'HEAD',
|
|
mode: 'no-cors',
|
|
// Set a short timeout to fail faster
|
|
signal: AbortSignal.timeout(800)
|
|
});
|
|
return true; // Connection successful
|
|
} catch (error) {
|
|
console.error('Ping attempt failed:', error);
|
|
return false; // Connection failed
|
|
}
|
|
};
|
|
|
|
// Try pinging multiple times with 1-second intervals
|
|
let isConnected = false;
|
|
const maxAttempts = 5;
|
|
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
connectBtnText.textContent = `Connecting... (${attempt}/${maxAttempts})`;
|
|
|
|
isConnected = await attemptPing();
|
|
if (isConnected) {
|
|
break; // Exit the loop if connection is successful
|
|
}
|
|
|
|
if (attempt < maxAttempts) {
|
|
// Wait 1 second before the next attempt
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
}
|
|
}
|
|
|
|
// Re-enable the button
|
|
connectListenerBtn.disabled = false;
|
|
|
|
if (isConnected) {
|
|
// Set to connected state
|
|
connectBtnText.textContent = 'Connected';
|
|
connectListenerBtn.classList.remove('btn-outline-secondary', 'btn-warning', 'btn-danger');
|
|
connectListenerBtn.classList.add('btn-success');
|
|
statusCircle.style.backgroundColor = '#198754'; // Darker green for the circle
|
|
} else {
|
|
// Set to offline state
|
|
connectBtnText.textContent = 'Connect';
|
|
connectListenerBtn.classList.remove('btn-outline-secondary', 'btn-warning', 'btn-success');
|
|
connectListenerBtn.classList.add('btn-danger');
|
|
statusCircle.style.backgroundColor = '#dc3545'; // Darker red for the circle
|
|
|
|
// Show error toast
|
|
showToast(`Could not connect to ${url}. Please check the URL and try again.`);
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
<script src="https://cdn.jsdelivr.net/npm/codemirror@5.65.2/lib/codemirror.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/codemirror@5.65.2/mode/javascript/javascript.js"></script>
|
|
<script src="script.js"></script>
|
|
</body>
|
|
</html>
|