Simplify web interface to execute-only, improve desktop editor UX

## Web Interface
- Remove Add/Edit functionality from web interface (execute-only now)
- Remove modal dialog and command builder
- Simplified JS from 480 to 267 lines
- Users can still create/edit macros in the desktop app

## Desktop Editor
- Fix Edit button padding (set fixed width of 50px)
- Capitalize key options for better readability (Enter, Tab, etc.)
- Display keys capitalized in command list

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-03 17:48:21 -08:00
parent a71c1f5ec4
commit 256e8c109c
3 changed files with 15 additions and 267 deletions

View File

@@ -26,7 +26,6 @@
<span>Disconnected</span>
</div>
<button class="header-btn secondary" onclick="app.refresh()">Refresh</button>
<button class="header-btn" onclick="app.openAddModal()">+ Add</button>
</div>
</header>
@@ -42,47 +41,6 @@
</div>
</main>
<!-- Modal -->
<div class="modal-overlay" id="modal-overlay" style="display: none;">
<div class="modal">
<div class="modal-header">
<h2 id="modal-title">Add Macro</h2>
<button class="modal-close" onclick="app.closeModal()">&times;</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="macro-name">Name</label>
<input type="text" id="macro-name" placeholder="Macro name">
</div>
<div class="form-group">
<label for="macro-category">Category (optional)</label>
<input type="text" id="macro-category" placeholder="Category">
</div>
<div class="form-group">
<label>Commands</label>
<div class="command-list" id="command-list">
<div class="empty-state"><p>No commands added yet</p></div>
</div>
<div class="add-command-btns">
<button class="add-command-btn" onclick="app.addCommand('text')">+ Text</button>
<button class="add-command-btn" onclick="app.addCommand('key')">+ Key</button>
<button class="add-command-btn" onclick="app.addCommand('hotkey')">+ Hotkey</button>
<button class="add-command-btn" onclick="app.addCommand('wait')">+ Wait</button>
<button class="add-command-btn" onclick="app.addCommand('app')">+ App</button>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger" id="delete-btn" style="display: none;"
onclick="app.deleteMacro(app.editingMacroId); app.closeModal();">
Delete
</button>
<button class="btn btn-secondary" onclick="app.closeModal()">Cancel</button>
<button class="btn btn-primary" onclick="app.saveMacro()">Save</button>
</div>
</div>
</div>
<!-- Toast Container -->
<div class="toast-container" id="toast-container"></div>

View File

@@ -1,4 +1,4 @@
// MacroPad PWA Application
// MacroPad PWA Application (Execute-only)
class MacroPadApp {
constructor() {
@@ -6,8 +6,6 @@ class MacroPadApp {
this.tabs = [];
this.currentTab = 'All';
this.ws = null;
this.editingMacroId = null;
this.commands = [];
this.init();
}
@@ -70,73 +68,6 @@ class MacroPadApp {
}
}
async saveMacro() {
const name = document.getElementById('macro-name').value.trim();
const category = document.getElementById('macro-category').value.trim();
if (!name) {
this.showToast('Please enter a macro name', 'error');
return;
}
if (this.commands.length === 0) {
this.showToast('Please add at least one command', 'error');
return;
}
const macroData = {
name,
category,
commands: this.commands
};
try {
let response;
if (this.editingMacroId) {
response = await fetch(`/api/macros/${this.editingMacroId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(macroData)
});
} else {
response = await fetch('/api/macros', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(macroData)
});
}
if (!response.ok) throw new Error('Save failed');
this.closeModal();
await this.loadTabs();
await this.loadMacros();
this.showToast('Macro saved successfully', 'success');
} catch (error) {
console.error('Error saving macro:', error);
this.showToast('Error saving macro', 'error');
}
}
async deleteMacro(macroId) {
if (!confirm('Are you sure you want to delete this macro?')) return;
try {
const response = await fetch(`/api/macros/${macroId}`, {
method: 'DELETE'
});
if (!response.ok) throw new Error('Delete failed');
await this.loadTabs();
await this.loadMacros();
this.showToast('Macro deleted', 'success');
} catch (error) {
console.error('Error deleting macro:', error);
this.showToast('Error deleting macro', 'error');
}
}
// WebSocket
setupWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
@@ -217,9 +148,7 @@ class MacroPadApp {
container.innerHTML = `
<div class="empty-state">
<p>No macros found</p>
<button class="btn btn-primary" onclick="app.openAddModal()">
Add Your First Macro
</button>
<p class="hint">Create macros in the desktop app</p>
</div>
`;
return;
@@ -233,9 +162,6 @@ class MacroPadApp {
return `
<div class="macro-card" data-macro-id="${id}" onclick="app.executeMacro('${id}')">
<button class="macro-edit-btn" onclick="event.stopPropagation(); app.openEditModal('${id}')">
Edit
</button>
${imageSrc
? `<img src="${imageSrc}" alt="${macro.name}" class="macro-image" onerror="this.style.display='none';this.nextElementSibling.style.display='flex'">`
: ''
@@ -249,132 +175,6 @@ class MacroPadApp {
}).join('');
}
renderCommandList() {
const container = document.getElementById('command-list');
if (!container) return;
if (this.commands.length === 0) {
container.innerHTML = '<div class="empty-state"><p>No commands added yet</p></div>';
return;
}
container.innerHTML = this.commands.map((cmd, index) => {
let displayValue = '';
switch (cmd.type) {
case 'text':
displayValue = cmd.value || '';
break;
case 'key':
displayValue = cmd.value || '';
break;
case 'hotkey':
displayValue = (cmd.keys || []).join(' + ');
break;
case 'wait':
displayValue = `${cmd.ms || 0}ms`;
break;
case 'app':
displayValue = cmd.command || '';
break;
}
return `
<div class="command-item" data-index="${index}">
<span class="command-type">${cmd.type}</span>
<span class="command-value">${displayValue}</span>
<div class="command-actions">
<button onclick="app.moveCommand(${index}, -1)">Up</button>
<button onclick="app.moveCommand(${index}, 1)">Down</button>
<button class="delete" onclick="app.removeCommand(${index})">X</button>
</div>
</div>
`;
}).join('');
}
// Command Builder
addCommand(type) {
let command = { type };
switch (type) {
case 'text':
const text = prompt('Enter text to type:');
if (!text) return;
command.value = text;
break;
case 'key':
const key = prompt('Enter key to press (e.g., enter, tab, escape, space):');
if (!key) return;
command.value = key.toLowerCase();
break;
case 'hotkey':
const keys = prompt('Enter key combination (comma separated, e.g., ctrl,c):');
if (!keys) return;
command.keys = keys.split(',').map(k => k.trim().toLowerCase());
break;
case 'wait':
const ms = prompt('Enter delay in milliseconds:');
if (!ms) return;
command.ms = parseInt(ms, 10) || 0;
break;
case 'app':
const appCmd = prompt('Enter application command:');
if (!appCmd) return;
command.command = appCmd;
break;
}
this.commands.push(command);
this.renderCommandList();
}
removeCommand(index) {
this.commands.splice(index, 1);
this.renderCommandList();
}
moveCommand(index, direction) {
const newIndex = index + direction;
if (newIndex < 0 || newIndex >= this.commands.length) return;
[this.commands[index], this.commands[newIndex]] =
[this.commands[newIndex], this.commands[index]];
this.renderCommandList();
}
// Modal
openAddModal() {
this.editingMacroId = null;
this.commands = [];
document.getElementById('macro-name').value = '';
document.getElementById('macro-category').value = '';
document.getElementById('modal-title').textContent = 'Add Macro';
document.getElementById('delete-btn').style.display = 'none';
this.renderCommandList();
document.getElementById('modal-overlay').style.display = 'flex';
}
async openEditModal(macroId) {
this.editingMacroId = macroId;
const macro = this.macros[macroId];
if (!macro) return;
document.getElementById('macro-name').value = macro.name || '';
document.getElementById('macro-category').value = macro.category || '';
document.getElementById('modal-title').textContent = 'Edit Macro';
document.getElementById('delete-btn').style.display = 'block';
this.commands = JSON.parse(JSON.stringify(macro.commands || []));
this.renderCommandList();
document.getElementById('modal-overlay').style.display = 'flex';
}
closeModal() {
document.getElementById('modal-overlay').style.display = 'none';
this.editingMacroId = null;
this.commands = [];
}
// Event Listeners
setupEventListeners() {
// Tab clicks
@@ -385,20 +185,6 @@ class MacroPadApp {
this.loadMacros();
}
});
// Modal close on overlay click
document.getElementById('modal-overlay')?.addEventListener('click', (e) => {
if (e.target.id === 'modal-overlay') {
this.closeModal();
}
});
// Escape key to close modal
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.closeModal();
}
});
}
// Toast notifications