Same fix as the relay: the local PWA re-fetched every macro image and revoked
the blob on each re-render. Cache image URL -> Promise<objectURL> and reuse
across renders (images are immutable per uuid path); add Cache-Control:
immutable on the desktop /api/image response. Verified in a browser: 7
re-renders produced only the initial 3 image fetches, none repeated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Minor PR-review follow-ups for the local PWA:
- Macro images load via a header-authenticated fetch -> blob object URL in both
token and relay modes, so no credential ever sits in an image URL; prior
object URLs are revoked on re-render.
- Local-mode auth failure now shows a clear "Access token required" state and
stops the reconnect/toast loop (one-shot), instead of retrying forever.
- CSP connect-src tightened from 'self' ws: wss: to 'self' (same-origin WS is
covered); verified the live WebSocket still connects.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
frame-ancestors is ignored inside a <meta> CSP, so move clickjacking
protection to real response headers (X-Frame-Options: DENY) and add
X-Content-Type-Options: nosniff and Referrer-Policy: no-referrer on all
responses. Drop the ineffective frame-ancestors token from the meta CSP.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Full semantic design-token layer (surfaces, spacing scale, radii, elevation,
type scale) as the single source of truth; electric-violet signature accent
instead of default blue.
- Light + dark themes via prefers-color-scheme; muted text now meets WCAG AA
(replaces low-contrast #aaa/#888).
- Macro cards read as physical keys (raised, hairline highlight, per-macro
--macro-color wash + placeholder, press/executing feedback, 2-line clamp).
- Proper modal sheet (blurred scrim, sticky header/footer, focus rings),
thumb-reachable floating action button, 44px tap targets, app-wide
:focus-visible, and prefers-reduced-motion support.
- manifest + theme-color meta updated to the new palette.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Security:
- Eliminate DOM-based XSS: renderMacros/renderTabs rebuilt with
createElement/textContent/setAttribute + addEventListener. No macro name,
id, image_path, or category ever reaches innerHTML.
- Remove all inline on*= handlers; add a strict Content-Security-Policy meta
(script-src 'self', object-src 'none', base-uri 'none', frame-ancestors
'none').
- Guard JSON.parse on WebSocket frames.
Robustness:
- Service worker: network-first for the app shell + JS/CSS so fixes actually
ship; synthetic 503 for offline API calls; drop no-op /ws intercept; bump
cache version.
- WebSocket reconnect: exponential backoff (1s→30s), tear down old socket
before reconnect, stop on pagehide/beforeunload.
- Tab-switch race fixed with a request-id guard; executing state cleared in
finally.
Accessibility:
- Macro cards and tabs are real buttons with aria-labels/roles; toast +
connection status are aria-live; removed user-scalable=no.
Feature — web-based macro creation/editing:
- Floating "+" button + modal command-sequence builder (text/key/hotkey/
wait/app) with reorder/remove; create via POST, edit via PUT (prefilled
from GET /api/macro/{id}), delete with a named confirm. Works in both local
and relay auth modes. Empty state now offers "Create your first macro".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The local FastAPI server previously exposed macro create/execute over the
network with no authentication while bound to 0.0.0.0, and macro `app`
commands ran via subprocess with shell=True — an unauthenticated RCE for
anyone on the LAN (and, via the relay, the internet).
- Mandatory per-install auth token (secrets.token_urlsafe) required on all
/api/* routes and the /ws handshake; timing-safe hmac.compare_digest.
PWA shell + /static assets remain public to bootstrap.
- Token auto-generated and persisted in settings.json (now chmod 0600).
- Bind host is configurable (web.allow_lan); token is the security boundary.
- macro `app` launch no longer uses shell=True — commands are tokenized with
shlex and exec'd directly, killing ;/&&/|/$() shell chaining.
- Relay client injects the local token and only forwards /api/* paths, and no
longer forwards attacker-supplied headers to the local server.
- Fixed path traversal in /api/image/{path} (realpath-confined to macro_images).
- GUI embeds the token in the LAN URL/QR; PWA reads it and sends it on API,
WebSocket, and image requests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1. Images not showing through relay:
- Use getApiUrl() for image paths in relay mode
- Add password as query param for img tags (can't use headers)
2. URL not updating in desktop app:
- Set _connected=True before on_session_id callback fires
- Ensures update_ip_label() shows relay URL immediately
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## New Features
### Minimize to Tray
- Window minimizes to system tray instead of taskbar
- Tray notification shown when minimized
- Double-click tray icon to restore
### Settings System
- New settings dialog (Edit > Settings or Ctrl+,)
- JSON-based settings persistence
- General tab: minimize to tray toggle
- Relay Server tab: enable/configure relay connection
### Relay Server Support
- New relay_client.py for connecting to relay server
- WebSocket client with auto-reconnection
- Forwards API requests to local server
- Updates QR code/URL when relay connected
### PWA Updates
- Added relay mode detection and authentication
- Password passed via header for API requests
- WebSocket authentication for relay connections
- Desktop status handling (connected/disconnected)
- Wake lock icon now always visible with status indicator
## Files Added
- gui/settings_manager.py
- gui/settings_dialog.py
- relay_client.py
## Dependencies
- Added aiohttp>=3.9.0 for relay client
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## Features
- Fullscreen button (⛶) in header to toggle fullscreen mode
- Wake Lock API integration to keep screen on while using the app
- Sun icon (☀) indicator shows wake lock status (bright = active)
## Wake Lock behavior
- Automatically requests wake lock when page loads
- Re-acquires wake lock when returning to the page
- Visual indicator pulses when active
- Gracefully hidden if Wake Lock API not supported
## Fullscreen
- Works on Android Chrome and desktop browsers
- iOS Safari has limited support (no fullscreen API)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## manifest.json
- Add `id` and `scope` fields for proper PWA identification
- Split icon purposes into separate entries (was "any maskable", now separate)
- Add `prefer_related_applications: false`
## index.html
- Add `viewport-fit=cover` for notched devices
- Add `mobile-web-app-capable` meta tag
- Add `application-name` and `msapplication` meta tags
- Add both 192px and 512px apple-touch-icon sizes
## styles.css
- Add safe-area-inset padding for notched devices (iPhone X+)
- Use 100dvh for proper mobile viewport height
- Add bottom safe area to toast container and macro grid
## service-worker.js
- Bump cache version to v2 to force update
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## 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>
## Major Changes
### Build System
- Replace requirements.txt with pyproject.toml for modern dependency management
- Support for uv package manager alongside pip
- Update PyInstaller spec files for new dependencies and structure
### Desktop GUI (Tkinter → PySide6)
- Complete rewrite of UI using PySide6/Qt6
- New modular structure in gui/ directory:
- main_window.py: Main application window
- macro_editor.py: Macro creation/editing dialog
- command_builder.py: Visual command sequence builder
- Modern dark theme with consistent styling
- System tray integration
### Web Server (Flask → FastAPI)
- Migrate from Flask/Waitress to FastAPI/Uvicorn
- Add WebSocket support for real-time updates
- Full CRUD API for macro management
- Image upload endpoint
### Web Interface → PWA
- New web/ directory with standalone static files
- PWA manifest and service worker for installability
- Offline caching support
- Full macro editing from web interface
- Responsive mobile-first design
- Command builder UI matching desktop functionality
### Macro System Enhancement
- New command sequence model replacing simple text/app types
- Command types: text, key, hotkey, wait, app
- Support for delays between commands (wait in ms)
- Support for key presses between commands (enter, tab, etc.)
- Automatic migration of existing macros to new format
- Backward compatibility maintained
### Files Added
- pyproject.toml
- gui/__init__.py, main_window.py, macro_editor.py, command_builder.py
- gui/widgets/__init__.py
- web/index.html, manifest.json, service-worker.js
- web/css/styles.css, web/js/app.js
- web/icons/icon-192.png, icon-512.png
### Files Removed
- requirements.txt (replaced by pyproject.toml)
- ui_components.py (replaced by gui/ modules)
- web_templates.py (replaced by web/ static files)
- main.spec (consolidated into platform-specific specs)
### Files Modified
- main.py: Simplified entry point for PySide6
- macro_manager.py: Command sequence model and migration
- web_server.py: FastAPI implementation
- config.py: Version bump to 0.9.0
- All .spec files: Updated for PySide6 and new structure
- README.md: Complete rewrite for v0.9.0
- .gitea/workflows/release.yml: Disabled pending build testing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>