Basic TELA App Template
Production-ready starter template using the XSWD connection patterns proven in tela_tests/app1. While the core WebSocket handling follows tested patterns, this template provides a modern dashboard UI designed for real applications, not API testing.
What's from tela_tests: XSWD connection, handshake, response handling
What's new: Modern UI, production features, dashboard components
π Template Structure
basic-tela-app/
βββ index.html # Main application (8.2KB)
βββ styles.css # TELA design system (6.8KB)
βββ app.js # Core functionality (9.1KB)
βββ README.md # InstructionsTotal size: ~24KB across 3 files (each under 18KB limit)
Proven XSWD Patterns from tela_tests/app1
- β
Global
socketvariable and WebSocket connection - β
applicationDatahandshake structure - β Traditional function declarations (tested and working)
- β
addEventListenerevent handling - β
response.accepted/response.result/response.errorlogic
New Production Features
- π Modern card-based dashboard UI
- π Wallet and network statistics display
- π Professional error handling components
- π Responsive design system
π index.html
File size: ~8.2KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My TELA Application</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="app-container">
<!-- Header Section -->
<div class="enhanced-card">
<div class="card-header">
<div style="display:flex;justify-content:space-between;align-items:center;">
<div>
<h1 style="color:#fff;font-size:1.8rem;font-weight:700;margin:0;">π My TELA App</h1>
<div class="section-info">A decentralized application on DERO blockchain</div>
</div>
<div id="connection-status" class="status-indicator status-info">
<div class="status-dot"></div>
<span>Connecting...</span>
</div>
</div>
</div>
</div>
<!-- Wallet Information -->
<div class="enhanced-card" id="wallet-section" style="display:none;">
<div class="card-header">
<h2 style="color:#52c8db;font-size:1.6rem;font-weight:700;margin:0;">π° Wallet Information</h2>
<div class="section-info">Connected wallet details and balance</div>
</div>
<div class="card-content">
<div class="stats-grid">
<div class="enhanced-stat-card">
<div id="wallet-balance" style="font-size:1.8rem;font-weight:700;color:#52c8db;margin-bottom:0.5rem;">
Loading...
</div>
<div style="color:#b3b3b3;font-size:0.9rem;text-transform:uppercase;letter-spacing:0.5px;">
Balance (DERO)
</div>
</div>
<div class="enhanced-stat-card">
<div id="wallet-address" style="font-size:1rem;font-weight:600;color:#52c8db;margin-bottom:0.5rem;word-break:break-all;">
Loading...
</div>
<div style="color:#b3b3b3;font-size:0.9rem;text-transform:uppercase;letter-spacing:0.5px;">
Wallet Address
</div>
</div>
</div>
</div>
</div>
<!-- Network Information -->
<div class="enhanced-card" id="network-section" style="display:none;">
<div class="card-header">
<h2 style="color:#b959b6;font-size:1.6rem;font-weight:700;margin:0;">π Network Information</h2>
<div class="section-info">Real-time DERO blockchain statistics</div>
</div>
<div class="card-content">
<div class="stats-grid">
<div class="enhanced-stat-card" style="border-color:rgba(185,89,182,0.3);">
<div id="network-height" style="font-size:1.8rem;font-weight:700;color:#b959b6;margin-bottom:0.5rem;">
Loading...
</div>
<div style="color:#b3b3b3;font-size:0.9rem;text-transform:uppercase;letter-spacing:0.5px;">
Block Height
</div>
</div>
<div class="enhanced-stat-card" style="border-color:rgba(185,89,182,0.3);">
<div id="network-difficulty" style="font-size:1.4rem;font-weight:700;color:#b959b6;margin-bottom:0.5rem;">
Loading...
</div>
<div style="color:#b3b3b3;font-size:0.9rem;text-transform:uppercase;letter-spacing:0.5px;">
Difficulty
</div>
</div>
<div class="enhanced-stat-card" style="border-color:rgba(185,89,182,0.3);">
<div id="network-hashrate" style="font-size:1.4rem;font-weight:700;color:#b959b6;margin-bottom:0.5rem;">
Loading...
</div>
<div style="color:#b3b3b3;font-size:0.9rem;text-transform:uppercase;letter-spacing:0.5px;">
Est. Hash Rate
</div>
</div>
</div>
</div>
</div>
<!-- Actions Section -->
<div class="enhanced-card" id="actions-section" style="display:none;">
<div class="card-header">
<h2 style="color:#4ade80;font-size:1.6rem;font-weight:700;margin:0;">β‘ Quick Actions</h2>
<div class="section-info">Common operations and utilities</div>
</div>
<div class="card-content">
<div class="actions">
<button class="btn-primary" onclick="refreshData()">π Refresh Data</button>
<button class="btn-success" onclick="showTransactionForm()">πΈ Send Transaction</button>
<button class="btn-warning" onclick="exportData()">π Export Data</button>
</div>
</div>
</div>
<!-- Error Display -->
<div id="error-display" style="display:none;" class="status-indicator status-error">
<div class="status-dot"></div>
<span id="error-message">Error message will appear here</span>
</div>
<!-- Loading Overlay -->
<div id="loading-overlay" style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.8);display:flex;align-items:center;justify-content:center;z-index:1000;">
<div style="text-align:center;color:#52c8db;">
<div style="font-size:2rem;margin-bottom:1rem;">π</div>
<div>Connecting to DERO wallet...</div>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>app.js
File size: ~9.1KB - Based on proven tela_tests patterns
// Basic TELA Application - Based on working tela_tests/app1 patterns
// Uses proven JavaScript patterns that successfully deploy to TELA
// Global WebSocket connection (proven pattern from tela_tests)
let socket;
// XSWD application data (matches tela_tests structure)
const applicationData = {
"id": "basic-tela-app-" + Date.now(),
"name": "Basic TELA Application",
"description": "Simple TELA app with wallet integration",
"url": "http://localhost:" + location.port
};
// Global variables for app state
let connected = false;
let walletData = null;
let networkData = null;
// Handle WebSocket connection (proven tela_tests pattern)
function connectWebSocket() {
console.log('π Connecting to DERO wallet via XSWD...');
socket = new WebSocket("ws://localhost:44326/xswd");
socket.addEventListener("open", function(event) {
console.log("β
WebSocket connection established:", event);
updateConnectionStatus("info", "π Requesting approval...");
sendData(applicationData);
});
let address = "";
let connecting = true;
socket.addEventListener("message", function(event) {
const response = JSON.parse(event.data);
console.log("π¨ Response received:", response);
if (response.accepted) {
console.log("π Connection accepted:", response.message);
connected = true;
updateConnectionStatus("success", "β
Connected");
// Request initial data
sendData({
jsonrpc: "2.0",
id: "1",
method: "GetAddress"
});
} else if (response.result) {
const res = response.result;
if (res.address) {
address = res.address;
console.log("π° Connected address:", address);
walletData = { address: address };
// Get balance
sendData({
jsonrpc: "2.0",
id: "2",
method: "GetBalance"
});
// Get network info
sendData({
jsonrpc: "2.0",
id: "3",
method: "GetInfo"
});
} else if (res.unlocked_balance !== undefined) {
const balance = res.unlocked_balance / 100000;
console.log("π° Balance:", balance.toFixed(5), "DERO");
if (walletData) {
walletData.balance = balance;
walletData.unlocked = res.balance / 100000;
}
updateWalletDisplay();
} else if (res.height) {
console.log("π Network height:", res.height);
networkData = {
height: res.height,
stableheight: res.stableheight,
difficulty: res.difficulty,
hashrate: Math.round(res.difficulty / 16)
};
updateNetworkDisplay();
showAllSections();
hideLoading();
connecting = false;
}
} else if (response.error) {
console.error("β Error:", response.error.message);
showError("Error: " + response.error.message);
if (connecting) updateConnectionStatus("error", "β Connection failed");
}
});
socket.addEventListener("error", function(event) {
console.error("π¨ WebSocket error:", event);
showError("Connection error");
updateConnectionStatus("error", "β Connection failed");
});
socket.addEventListener("close", function(event) {
console.log("π WebSocket closed:", event.code, event.reason);
connected = false;
updateConnectionStatus("error", "β Disconnected");
});
}
// Send data through WebSocket (proven tela_tests pattern)
function sendData(data) {
if (socket && socket.readyState === WebSocket.OPEN) {
try {
socket.send(JSON.stringify(data));
if (data.method) {
console.log(data.method, "request sent");
}
} catch (error) {
console.error("Failed to send data:", error);
showError("Failed to send request");
}
} else {
console.log("WebSocket not open. State:", socket ? socket.readyState : "N/A");
showError("Wallet not connected");
}
}
// Update UI functions
function updateWalletDisplay() {
if (walletData) {
document.getElementById('wallet-balance').textContent = walletData.balance.toFixed(5);
const addr = walletData.address;
const shortAddr = addr.substring(0, 12) + '...' + addr.substring(addr.length - 12);
document.getElementById('wallet-address').textContent = shortAddr;
}
}
function updateNetworkDisplay() {
if (networkData) {
document.getElementById('network-height').textContent = networkData.height.toLocaleString();
document.getElementById('network-difficulty').textContent = networkData.difficulty.toLocaleString();
document.getElementById('network-hashrate').textContent = (networkData.hashrate / 1000000).toFixed(2) + ' MH/s';
}
}
function showAllSections() {
document.getElementById('wallet-section').style.display = 'block';
document.getElementById('network-section').style.display = 'block';
document.getElementById('actions-section').style.display = 'block';
}
function updateConnectionStatus(type, message) {
const statusEl = document.getElementById('connection-status');
statusEl.className = `status-indicator status-${type}`;
statusEl.querySelector('span').textContent = message;
}
function showError(message) {
console.error('π¨ Application error:', message);
const errorEl = document.getElementById('error-display');
document.getElementById('error-message').textContent = message;
errorEl.style.display = 'flex';
// Auto-hide after 8 seconds
setTimeout(function() {
errorEl.style.display = 'none';
}, 8000);
}
function hideLoading() {
document.getElementById('loading-overlay').style.display = 'none';
}
// Global functions for button actions (proven tela_tests pattern)
function refreshData() {
if (!connected) {
showError('Wallet not connected');
return;
}
console.log('π Refreshing data...');
// Refresh balance
sendData({
jsonrpc: "2.0",
id: "refresh-balance",
method: "GetBalance"
});
// Refresh network info
sendData({
jsonrpc: "2.0",
id: "refresh-info",
method: "GetInfo"
});
}
function showTransactionForm() {
alert('π‘ Transaction Demo\n\nThis would open a transaction form where users could:\nβ’ Enter recipient address\nβ’ Specify amount\nβ’ Set transaction parameters\nβ’ Confirm and send via XSWD\n\nImplement your transaction UI here!');
}
function exportData() {
if (networkData && walletData) {
const data = {
timestamp: new Date().toISOString(),
network: networkData,
wallet: {
address: walletData.address,
balance: walletData.balance
}
};
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'tela-app-data.json';
a.click();
URL.revokeObjectURL(url);
} else {
showError('No data to export');
}
}
// Initialize application when page loads (proven tela_tests pattern)
document.addEventListener('DOMContentLoaded', function() {
console.log('π Initializing TELA application...');
connectWebSocket();
});
// Cleanup when page unloads
window.addEventListener('beforeunload', function() {
if (socket) {
socket.close();
}
});styles.css
File size: ~6.8KB - Same as before (CSS is not affected by JavaScript patterns)
/* TELA Basic App Styles - Complete CSS Framework */
/* Reset and Base */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
scroll-behavior: smooth;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
body {
background: linear-gradient(135deg, #0a0c0e, #15171a, #1d2024);
min-height: 100vh;
color: #ffffff;
overflow-x: hidden;
}
/* Layout */
.app-container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 1rem;
min-height: 100vh;
}
/* Enhanced Card System */
.enhanced-card {
background: rgba(0,0,0,0.4);
border: 1px solid rgba(82,200,219,0.2);
border-radius: 16px;
backdrop-filter: blur(10px);
margin: 1rem 0 2rem 0;
overflow: hidden;
}
.card-header {
padding: 1.5rem 1.5rem 1rem 1.5rem;
border-bottom: 1px solid rgba(82,200,219,0.1);
}
.card-content {
padding: 1.5rem;
}
.section-info {
color: #b3b3b3;
font-size: 0.9rem;
margin-top: 0.5rem;
line-height: 1.4;
}
/* Statistics Grid */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1rem;
}
.enhanced-stat-card {
background: rgba(0,0,0,0.2);
border: 1px solid rgba(82,200,219,0.3);
border-radius: 8px;
padding: 1.5rem;
text-align: center;
transition: all 0.2s ease;
}
.enhanced-stat-card:hover {
border-color: rgba(82,200,219,0.5);
transform: translateY(-2px);
}
/* Button System */
.btn-primary, .btn-success, .btn-warning, .btn-danger {
padding: 0.6rem 1.2rem;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s ease;
font-weight: 500;
text-decoration: none;
display: inline-block;
font-size: 0.9rem;
border: 1px solid;
}
.btn-primary {
background: rgba(82,200,219,0.1);
border-color: #52c8db;
color: #52c8db;
}
.btn-primary:hover {
background: rgba(82,200,219,0.2);
}
.btn-success {
background: rgba(74,222,128,0.1);
border-color: #4ade80;
color: #4ade80;
}
.btn-success:hover {
background: rgba(74,222,128,0.2);
}
.btn-warning {
background: rgba(251,191,36,0.1);
border-color: #fbbf24;
color: #fbbf24;
}
.btn-warning:hover {
background: rgba(251,191,36,0.2);
}
/* Status Indicators */
.status-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.6rem 1rem;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.status-success {
background: rgba(74,222,128,0.1);
border: 1px solid #4ade80;
color: #4ade80;
}
.status-info {
background: rgba(82,200,219,0.1);
border: 1px solid #52c8db;
color: #52c8db;
}
.status-warning {
background: rgba(251,191,36,0.1);
border: 1px solid #fbbf24;
color: #fbbf24;
}
.status-error {
background: rgba(239,68,68,0.1);
border: 1px solid #ef4444;
color: #ef4444;
}
.status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: currentColor;
}
/* Actions Layout */
.actions {
display: flex;
gap: 0.75rem;
flex-wrap: wrap;
margin-top: 1rem;
}
/* Responsive Design */
@media (max-width: 768px) {
.app-container {
padding: 0.5rem;
}
.stats-grid {
grid-template-columns: 1fr;
gap: 0.75rem;
}
.actions {
flex-direction: column;
}
.card-header, .card-content {
padding: 1rem;
}
.enhanced-stat-card {
padding: 1rem;
}
}
/* Utility Classes */
.text-center { text-align: center; }
.mb-1 { margin-bottom: 1rem; }
.hidden { display: none; }π Template Architecture
β XSWD Connection (from tela_tests/app1)
The core WebSocket connection code follows the proven patterns from working tela_tests:
-
Connection Pattern:
// Same approach as tela_tests/app1 lines 71-80 socket = new WebSocket("ws://localhost:44326/xswd"); socket.addEventListener("open", function(event) { sendData(applicationData); }); -
Response Handling:
// Same logic structure as tela_tests/app1 lines 86-145 if (response.accepted) { /* connection approved */ } else if (response.result) { /* API response data */ } else if (response.error) { /* handle errors */ } -
Traditional JavaScript:
- Global variables (
let socket,const applicationData) - Function declarations (
function connectWebSocket(),function sendData()) - Standard event listeners (
addEventListener)
- Global variables (