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

@@ -65,6 +65,7 @@ class CommandItem(QWidget):
""" """
edit_btn = QPushButton("Edit") edit_btn = QPushButton("Edit")
edit_btn.setFixedWidth(50)
edit_btn.setStyleSheet(btn_style) edit_btn.setStyleSheet(btn_style)
edit_btn.clicked.connect(self.edit_clicked.emit) edit_btn.clicked.connect(self.edit_clicked.emit)
layout.addWidget(edit_btn) layout.addWidget(edit_btn)
@@ -104,9 +105,11 @@ class CommandItem(QWidget):
if cmd_type == "text": if cmd_type == "text":
return self.command.get("value", "")[:50] return self.command.get("value", "")[:50]
elif cmd_type == "key": elif cmd_type == "key":
return self.command.get("value", "") key = self.command.get("value", "")
return key.capitalize() if key else ""
elif cmd_type == "hotkey": elif cmd_type == "hotkey":
return " + ".join(self.command.get("keys", [])) keys = self.command.get("keys", [])
return " + ".join(k.capitalize() for k in keys)
elif cmd_type == "wait": elif cmd_type == "wait":
return f"{self.command.get('ms', 0)}ms" return f"{self.command.get('ms', 0)}ms"
elif cmd_type == "app": elif cmd_type == "app":
@@ -207,9 +210,9 @@ class CommandBuilder(QWidget):
elif cmd_type == "key": elif cmd_type == "key":
from PySide6.QtWidgets import QInputDialog from PySide6.QtWidgets import QInputDialog
keys = ["enter", "tab", "escape", "space", "backspace", "delete", keys = ["Enter", "Tab", "Escape", "Space", "Backspace", "Delete",
"up", "down", "left", "right", "home", "end", "pageup", "pagedown", "Up", "Down", "Left", "Right", "Home", "End", "PageUp", "PageDown",
"f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12"] "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12"]
key, ok = QInputDialog.getItem(self, "Key Command", "Select key:", keys, 0, True) key, ok = QInputDialog.getItem(self, "Key Command", "Select key:", keys, 0, True)
if not ok or not key: if not ok or not key:
return return
@@ -282,10 +285,11 @@ class CommandBuilder(QWidget):
cmd["value"] = text cmd["value"] = text
elif cmd_type == "key": elif cmd_type == "key":
keys = ["enter", "tab", "escape", "space", "backspace", "delete", keys = ["Enter", "Tab", "Escape", "Space", "Backspace", "Delete",
"up", "down", "left", "right", "home", "end", "pageup", "pagedown", "Up", "Down", "Left", "Right", "Home", "End", "PageUp", "PageDown",
"f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12"] "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12"]
current = keys.index(cmd.get("value", "enter")) if cmd.get("value") in keys else 0 keys_lower = [k.lower() for k in keys]
current = keys_lower.index(cmd.get("value", "enter")) if cmd.get("value") in keys_lower else 0
key, ok = QInputDialog.getItem(self, "Edit Key", "Select key:", keys, current, True) key, ok = QInputDialog.getItem(self, "Edit Key", "Select key:", keys, current, True)
if ok and key: if ok and key:
cmd["value"] = key.lower() cmd["value"] = key.lower()

View File

@@ -26,7 +26,6 @@
<span>Disconnected</span> <span>Disconnected</span>
</div> </div>
<button class="header-btn secondary" onclick="app.refresh()">Refresh</button> <button class="header-btn secondary" onclick="app.refresh()">Refresh</button>
<button class="header-btn" onclick="app.openAddModal()">+ Add</button>
</div> </div>
</header> </header>
@@ -42,47 +41,6 @@
</div> </div>
</main> </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 --> <!-- Toast Container -->
<div class="toast-container" id="toast-container"></div> <div class="toast-container" id="toast-container"></div>

View File

@@ -1,4 +1,4 @@
// MacroPad PWA Application // MacroPad PWA Application (Execute-only)
class MacroPadApp { class MacroPadApp {
constructor() { constructor() {
@@ -6,8 +6,6 @@ class MacroPadApp {
this.tabs = []; this.tabs = [];
this.currentTab = 'All'; this.currentTab = 'All';
this.ws = null; this.ws = null;
this.editingMacroId = null;
this.commands = [];
this.init(); 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 // WebSocket
setupWebSocket() { setupWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
@@ -217,9 +148,7 @@ class MacroPadApp {
container.innerHTML = ` container.innerHTML = `
<div class="empty-state"> <div class="empty-state">
<p>No macros found</p> <p>No macros found</p>
<button class="btn btn-primary" onclick="app.openAddModal()"> <p class="hint">Create macros in the desktop app</p>
Add Your First Macro
</button>
</div> </div>
`; `;
return; return;
@@ -233,9 +162,6 @@ class MacroPadApp {
return ` return `
<div class="macro-card" data-macro-id="${id}" onclick="app.executeMacro('${id}')"> <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 ${imageSrc
? `<img src="${imageSrc}" alt="${macro.name}" class="macro-image" onerror="this.style.display='none';this.nextElementSibling.style.display='flex'">` ? `<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(''); }).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 // Event Listeners
setupEventListeners() { setupEventListeners() {
// Tab clicks // Tab clicks
@@ -385,20 +185,6 @@ class MacroPadApp {
this.loadMacros(); 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 // Toast notifications