This commit is contained in:
2025-05-24 10:33:50 +04:00
parent 2ee8a95a90
commit c9b14730ad
8 changed files with 845 additions and 0 deletions

View File

@@ -0,0 +1,144 @@
/* OpenRPC UI Styles */
.method-tree {
max-height: 600px;
overflow-y: auto;
border-right: 1px solid #dee2e6;
}
.method-item {
cursor: pointer;
padding: 8px 15px;
border-radius: 4px;
transition: background-color 0.2s ease;
}
.method-item:hover {
background-color: #f8f9fa;
}
.method-item.active {
background-color: #e9ecef;
font-weight: bold;
border-left: 3px solid #0d6efd;
}
.param-card {
margin-bottom: 15px;
border: 1px solid #dee2e6;
border-radius: 4px;
}
.result-container {
max-height: 400px;
overflow-y: auto;
margin-top: 20px;
padding: 15px;
border: 1px solid #dee2e6;
border-radius: 4px;
background-color: #f8f9fa;
}
.code-editor {
font-family: 'Courier New', Courier, monospace;
min-height: 200px;
max-height: 400px;
overflow-y: auto;
white-space: pre;
font-size: 14px;
line-height: 1.5;
padding: 10px;
border: 1px solid #ced4da;
border-radius: 4px;
background-color: #f8f9fa;
}
.schema-table {
font-size: 0.9rem;
width: 100%;
margin-bottom: 1rem;
}
.schema-table th {
font-weight: 600;
background-color: #f8f9fa;
}
.schema-required {
color: #dc3545;
font-weight: bold;
}
.schema-optional {
color: #6c757d;
}
.method-description {
font-size: 0.9rem;
color: #6c757d;
margin-bottom: 15px;
}
.section-header {
font-size: 1.1rem;
font-weight: 600;
margin-top: 20px;
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px solid #dee2e6;
}
.example-container {
margin-bottom: 15px;
}
.example-header {
font-weight: 600;
margin-bottom: 5px;
}
.example-content {
background-color: #f8f9fa;
padding: 10px;
border-radius: 4px;
overflow-x: auto;
}
/* Socket path input styling */
.socket-path-container {
margin-bottom: 15px;
}
.socket-path-label {
font-weight: 600;
margin-bottom: 5px;
}
/* Execute button styling */
.execute-button {
margin-top: 10px;
}
/* Response styling */
.response-success {
border-left: 4px solid #28a745;
}
.response-error {
border-left: 4px solid #dc3545;
}
/* Loading indicator */
.loading-spinner {
display: inline-block;
width: 1rem;
height: 1rem;
border: 0.2em solid currentColor;
border-right-color: transparent;
border-radius: 50%;
animation: spinner-border .75s linear infinite;
}
@keyframes spinner-border {
to { transform: rotate(360deg); }
}

View File

@@ -0,0 +1,141 @@
/**
* OpenRPC UI JavaScript
* Handles the interactive functionality of the OpenRPC UI
*/
document.addEventListener('DOMContentLoaded', function() {
// Initialize form elements
const specForm = document.getElementById('specForm');
const rpcForm = document.getElementById('rpcForm');
const paramsEditor = document.getElementById('paramsEditor');
const resultContainer = document.getElementById('resultContainer');
const resultOutput = document.getElementById('resultOutput');
const errorContainer = document.getElementById('errorContainer');
const errorOutput = document.getElementById('errorOutput');
// Format JSON in the parameters editor
if (paramsEditor && paramsEditor.value) {
try {
const params = JSON.parse(paramsEditor.value);
paramsEditor.value = JSON.stringify(params, null, 2);
} catch (e) {
// If not valid JSON, leave as is
console.warn('Could not format parameters as JSON:', e);
}
}
// Handle RPC execution
if (rpcForm) {
rpcForm.addEventListener('submit', function(e) {
e.preventDefault();
// Hide previous results
if (resultContainer) resultContainer.classList.add('d-none');
if (errorContainer) errorContainer.classList.add('d-none');
// Get form data
const spec = document.getElementById('spec').value;
const method = document.querySelector('input[name="selectedMethod"]').value;
const socketPath = document.getElementById('socketPath').value;
const paramsText = paramsEditor.value;
// Show loading indicator
const submitButton = rpcForm.querySelector('button[type="submit"]');
const originalButtonText = submitButton.innerHTML;
submitButton.disabled = true;
submitButton.innerHTML = '<span class="loading-spinner me-2"></span>Executing...';
// Validate
if (!spec || !method || !socketPath) {
showError('Missing required fields: spec, method, or socketPath');
resetButton();
return;
}
// Parse params
let params;
try {
params = JSON.parse(paramsText);
} catch (e) {
showError('Invalid JSON parameters: ' + e.message);
resetButton();
return;
}
// Execute RPC
fetch('/api/rpcui/execute', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
spec: spec,
method: method,
socketPath: socketPath,
params: params
})
})
.then(response => response.json())
.then(data => {
if (data.error) {
showError(data.error);
} else {
showResult(data.result);
}
})
.catch(error => {
showError('Request failed: ' + error.message);
})
.finally(() => {
resetButton();
});
function resetButton() {
submitButton.disabled = false;
submitButton.innerHTML = originalButtonText;
}
function showError(message) {
if (errorContainer && errorOutput) {
errorContainer.classList.remove('d-none');
errorOutput.textContent = message;
}
}
function showResult(result) {
if (resultContainer && resultOutput) {
resultContainer.classList.remove('d-none');
resultOutput.textContent = JSON.stringify(result, null, 2);
}
}
});
}
// Method tree navigation
const methodItems = document.querySelectorAll('.method-item');
methodItems.forEach(item => {
item.addEventListener('click', function(e) {
// Already handled by href, but could add additional functionality here
});
});
// Format JSON examples
const jsonExamples = document.querySelectorAll('pre code');
jsonExamples.forEach(example => {
try {
const content = example.textContent;
const json = JSON.parse(content);
example.textContent = JSON.stringify(json, null, 2);
} catch (e) {
// If not valid JSON, leave as is
console.warn('Could not format example as JSON:', e);
}
});
// Add syntax highlighting if a library like highlight.js is available
if (typeof hljs !== 'undefined') {
document.querySelectorAll('pre code').forEach((block) => {
hljs.highlightBlock(block);
});
}
});