This update will need to get cleaned up, but for right now we are doing testing.
292 lines
6.2 KiB
Python
292 lines
6.2 KiB
Python
# HTML templates for the web interface
|
|
|
|
INDEX_HTML = '''
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MacroPad Web Interface</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 0;
|
|
padding: 20px;
|
|
background-color: #2e2e2e;
|
|
color: #ffffff;
|
|
}
|
|
.header-container {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
h1 {
|
|
color: #007acc;
|
|
margin: 0;
|
|
}
|
|
.refresh-button {
|
|
background-color: #505050;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 10px 15px;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.refresh-button:hover {
|
|
background-color: #007acc;
|
|
}
|
|
.refresh-button svg {
|
|
margin-right: 5px;
|
|
}
|
|
.tab-container {
|
|
margin-bottom: 20px;
|
|
border-bottom: 2px solid #404040;
|
|
}
|
|
.tab-list {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 5px;
|
|
margin-bottom: 0;
|
|
}
|
|
.tab-button {
|
|
background-color: #404040;
|
|
color: #ffffff;
|
|
border: none;
|
|
border-radius: 8px 8px 0 0;
|
|
padding: 10px 15px;
|
|
font-size: 14px;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s;
|
|
border-bottom: 3px solid transparent;
|
|
}
|
|
.tab-button:hover {
|
|
background-color: #505050;
|
|
}
|
|
.tab-button.active {
|
|
background-color: #007acc;
|
|
border-bottom-color: #007acc;
|
|
}
|
|
.macro-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
gap: 15px;
|
|
margin-top: 20px;
|
|
}
|
|
.macro-button {
|
|
background-color: #505050;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
padding: 15px 10px;
|
|
text-align: center;
|
|
font-size: 16px;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-height: 100px;
|
|
}
|
|
.macro-button:hover, .macro-button:active {
|
|
background-color: #007acc;
|
|
}
|
|
.macro-button img {
|
|
max-width: 64px;
|
|
max-height: 64px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.status {
|
|
text-align: center;
|
|
margin: 20px 0;
|
|
padding: 10px;
|
|
border-radius: 5px;
|
|
}
|
|
.success {
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
display: none;
|
|
}
|
|
.error {
|
|
background-color: #f44336;
|
|
color: white;
|
|
display: none;
|
|
}
|
|
@media (max-width: 600px) {
|
|
.macro-grid {
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
}
|
|
.macro-button {
|
|
padding: 10px 5px;
|
|
font-size: 14px;
|
|
}
|
|
h1 {
|
|
font-size: 24px;
|
|
}
|
|
.refresh-button {
|
|
padding: 8px 12px;
|
|
font-size: 14px;
|
|
}
|
|
.tab-button {
|
|
padding: 8px 12px;
|
|
font-size: 12px;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="header-container">
|
|
<h1>MacroPad Web Interface</h1>
|
|
<button class="refresh-button" onclick="loadTabs()">
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
|
<path d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
|
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
|
</svg>
|
|
Refresh
|
|
</button>
|
|
</div>
|
|
<div class="status success" id="success-status">Macro executed successfully!</div>
|
|
<div class="status error" id="error-status">Failed to execute macro</div>
|
|
<div class="tab-container">
|
|
<div class="tab-list" id="tab-list">
|
|
<!-- Tabs will be loaded here -->
|
|
</div>
|
|
</div>
|
|
<div class="macro-grid" id="macro-grid">
|
|
<!-- Macros will be loaded here -->
|
|
</div>
|
|
<script>
|
|
let currentTab = 'All';
|
|
let allMacros = {};
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
loadTabs();
|
|
});
|
|
|
|
function loadTabs() {
|
|
fetch('/api/tabs')
|
|
.then(response => response.json())
|
|
.then(tabs => {
|
|
const tabList = document.getElementById('tab-list');
|
|
tabList.innerHTML = '';
|
|
|
|
tabs.forEach(tab => {
|
|
const button = document.createElement('button');
|
|
button.className = 'tab-button';
|
|
button.textContent = tab;
|
|
button.onclick = function() { switchTab(tab); };
|
|
|
|
if (tab === currentTab) {
|
|
button.classList.add('active');
|
|
}
|
|
|
|
tabList.appendChild(button);
|
|
});
|
|
|
|
// Load macros for current tab
|
|
loadMacros(currentTab);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading tabs:', error);
|
|
});
|
|
}
|
|
|
|
function switchTab(tabName) {
|
|
currentTab = tabName;
|
|
|
|
// Update tab button states
|
|
const tabButtons = document.querySelectorAll('.tab-button');
|
|
tabButtons.forEach(button => {
|
|
button.classList.remove('active');
|
|
if (button.textContent === tabName) {
|
|
button.classList.add('active');
|
|
}
|
|
});
|
|
|
|
// Load macros for selected tab
|
|
loadMacros(tabName);
|
|
}
|
|
|
|
function loadMacros(tab = 'All') {
|
|
const url = tab === 'All' ? '/api/macros' : `/api/macros/${encodeURIComponent(tab)}`;
|
|
|
|
fetch(url)
|
|
.then(response => response.json())
|
|
.then(macros => {
|
|
allMacros = macros;
|
|
displayMacros(macros);
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading macros:', error);
|
|
});
|
|
}
|
|
|
|
function displayMacros(macros) {
|
|
const macroGrid = document.getElementById('macro-grid');
|
|
macroGrid.innerHTML = '';
|
|
|
|
if (Object.keys(macros).length === 0) {
|
|
macroGrid.innerHTML = '<p style="grid-column: 1 / -1; text-align: center;">No macros in this category.</p>';
|
|
return;
|
|
}
|
|
|
|
for (const [macroId, macro] of Object.entries(macros)) {
|
|
const button = document.createElement('button');
|
|
button.className = 'macro-button';
|
|
button.onclick = function() { executeMacro(macroId); };
|
|
|
|
// Add image if available
|
|
if (macro.image_path) {
|
|
const img = document.createElement('img');
|
|
img.src = `/api/image/${encodeURIComponent(macro.image_path)}`;
|
|
img.alt = macro.name;
|
|
img.onerror = function() {
|
|
this.style.display = 'none';
|
|
};
|
|
button.appendChild(img);
|
|
}
|
|
|
|
const text = document.createTextNode(macro.name);
|
|
button.appendChild(text);
|
|
macroGrid.appendChild(button);
|
|
}
|
|
}
|
|
|
|
function executeMacro(macroId) {
|
|
fetch('/api/execute', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({ macro_id: macroId })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
const successStatus = document.getElementById('success-status');
|
|
const errorStatus = document.getElementById('error-status');
|
|
if (data.success) {
|
|
successStatus.style.display = 'block';
|
|
errorStatus.style.display = 'none';
|
|
setTimeout(() => { successStatus.style.display = 'none'; }, 2000);
|
|
} else {
|
|
errorStatus.style.display = 'block';
|
|
successStatus.style.display = 'none';
|
|
setTimeout(() => { errorStatus.style.display = 'none'; }, 2000);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error executing macro:', error);
|
|
const errorStatus = document.getElementById('error-status');
|
|
errorStatus.style.display = 'block';
|
|
setTimeout(() => { errorStatus.style.display = 'none'; }, 2000);
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
''' |