- Add xclip to install.sh dependencies (was missing entirely — root cause)
- Poll both CLIPBOARD and PRIMARY X11 selections (Electron button-click
copies often use PRIMARY)
- Add xsel as fallback if xclip unavailable
- Log xclip errors with stderr instead of silently swallowing
- Add startup diagnostic test for clipboard connectivity
- Replace silent clipboard write with visible toast notification:
on HTTPS auto-copies and shows brief "Copied" confirmation,
on HTTP shows "Click to copy" button (user gesture enables clipboard)
- Toast auto-dismisses after 8s, styled to match Atlus theme
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
X11 → Browser:
- Background task polls xclip every 500ms for clipboard changes
- When content changes, sends {"type":"clipboard","data":"..."} via WS
- Browser receives and writes to navigator.clipboard
Browser → X11:
- Ctrl+V reads navigator.clipboard.readText() first
- Sends clipboard content to backend via WS message
- Backend writes to X11 clipboard via xclip -selection clipboard
- Then forwards the Ctrl+V keystroke so the app pastes
This allows copying links/text from GUI apps (like Nextcloud auth URLs)
and pasting content from the host browser into the GUI app.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root causes fixed:
- _focused was a stray class annotation in @dataclass, causing field
ordering issues — moved to proper dataclass field
- xdotool type --window WID not supported on all versions — removed
--window flag, use focused window instead
- xdotool commands with --window may fail silently — switched to
absolute coordinates (window is at 0,0 filling the display)
- All xdotool errors were silently swallowed — now logged with stderr
Mouse events:
- Use absolute mousemove + click (no --window) since window fills display
- Separate mousemove and click into two calls for reliability
- Fire-and-forget for mousemove to reduce latency
Keyboard events:
- xdotool type (no --window) for printable characters
- xdotool key (no --window) for special keys and modifier combos
- Window focused once via _ensure_focus, not per-event
Diagnostics:
- Backend logs first 5 input events received per WebSocket session
- Backend logs xdotool stderr on failure
- Frontend logs first 10 input events sent + WS state warnings
- Frontend uses capture phase for keyboard events
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- After window discovery, activate, move to (0,0), and resize to
fill the 1280x1024 Xvfb display so the app is fullscreen
- Add windowactivate --sync before click events so the window
receives focus in X11 before mouse input
- Add windowfocus --sync before key events so keyboard input
goes to the correct window
- Make canvas fill the entire app panel area (width/height 100%)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Window discovery:
- Add display-wide search as final fallback (any window on the Xvfb)
- Also search by WM_CLASS for Electron apps
- Increase timeout to 60s, log progress and early crashes
- Add restart endpoint POST /api/display/apps/{id}/restart
Panel apps:
- Replace play/stop button with ⋮ context menu (Start, Stop, Restart)
- LED indicator: green=running, amber pulsing=starting, red=stopped/error
- Add status field to app API response
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Removed individual delete, upload, hidden files, and refresh buttons.
Added a hamburger (☰) menu with:
- Select: toggles selection mode with checkboxes for multi-select
- Delete: enabled only when files are selected
- Upload: file upload
- Refresh: reload current directory
- Show Hidden Files: toggles dotfile visibility, persisted via localStorage
Toolbar now only has New File, New Folder, and the hamburger menu for a
cleaner interface.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The middle-dot character was too small at 14px. Use a larger period
with explicit 20px font size so the button is actually visible in
the toolbar.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Toolbar button (·) toggles visibility of dotfiles. Hidden by default,
click to show. Active state highlighted with accent color. Filtering
happens client-side so no backend changes needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Restructured loadPanelApps() to use per-operation try/catch instead of
one giant try/catch that silently swallowed errors. The Add button is now
appended unconditionally after the app list, so it always remains visible.
Each app row renders independently — a single bad app config won't break
the entire panel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Ethernet watchdog: background task polls /sys/class/net every 5s, detects
cable plug-in on disabled interfaces and auto-enables with DHCP
- interface_down endpoint now refuses to disable the only active interface
(returns 409), preventing accidental lockout
- Frontend shows the error message instead of silently failing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Capture stderr from GUI apps and show meaningful exit reasons (signal names,
error messages) instead of generic "Application exited"
- Add /api/display/discover-apps endpoint that scans .desktop files for
installed GUI apps, replacing manual-only app configuration
- Settings > Applications now shows discoverable apps with one-click Add,
with manual form available as fallback
- Fix WiFi: start wpa_supplicant before bringing interface up, handle rfkill
blocks, add retry logic and error messages to scan results
- Fix WiFi scan frontend bug: response is {networks:[...]} not a bare array
- Add iw and wpasupplicant to install.sh dependencies
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove dock button creation from display.js initGuiApps (apps belong in right panel)
- Register GUI app modules dynamically in loadPanelApps for newly added apps
- Refresh right panel immediately after adding/removing apps in Settings
- Expose loadPanelApps globally for cross-module panel refresh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Panel: SERVICES section becomes APPLICATIONS — shows configured GUI apps with
status dots, launch/stop controls, and "+ Add" button linking to Settings.
Backend: DisplayManager.autostart_apps() launches autostart-enabled GUI apps on
service startup (always-on desktop session). Lifespan calls it before yield.
Settings: new Applications section for managing GUI apps (add/remove/autostart
toggle). General section gains update scanner interval + enable/disable toggle.
Config adds update_check_enabled and update_check_interval fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend: wireless interface detection, wpa_supplicant config writing, WiFi scan
endpoint via iw/iwlist. Frontend: segmented radio selector for DHCP/Static (fixes
text overflow behind right panel), SSID/password fields for wireless interfaces
with network scan dropdown, Apply handler sends WiFi credentials.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each configured GUI app (e.g. Nextcloud) gets its own dock icon and
opens as a regular Atlus tab. Under the hood: Xvfb virtual display,
ImageMagick captures individual window pixmaps as JPEG, streams over
WebSocket to a canvas element, with xdotool forwarding mouse/keyboard
input back to the X11 window. Apps persist in background when tab is
closed, and streaming pauses when no viewers are attached.
New files: backend/display.py (DisplayManager + ManagedGuiApp),
backend/routers/display.py (WebSocket + REST), frontend display.js/css.
Config: gui_apps array in settings for registered applications.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Xvfb + x11vnc + websockify + noVNC approach defeats the purpose
of a native web desktop environment. Removed all related backend
(display.py, routers/display.py), frontend (xdisplay.js/css, panel
tray polling), and installer (X11 deps) code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace dismissable toast with persistent status row showing a green
dot + "Up to date" with commit hash, or amber dot + "Update available"
with an inline Install button. One or the other is always visible.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
shutil.which("git") fails under systemd's stripped PATH. Added
_find_git() that checks common locations (/usr/bin/git, etc.)
as fallback. Also added null-safe check on apiFetch response
in the update checker frontend code.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix asyncio.get_event_loop() → get_running_loop() in PTY reader
- Add error logging for PTY spawn failures
- Add POST /api/session/state endpoint for sendBeacon (beforeunload)
- Use navigator.sendBeacon for reliable state save on page close
- Improve frontend error reporting when terminal creation fails
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PTY terminals now survive browser refresh and close. Session manager
owns PTY lifecycle independently of WebSocket connections, with
background readers storing scrollback for replay on reconnect. Desktop
state (open apps, active app, terminal tabs) persists server-side and
restores automatically on login. Auth tokens moved to localStorage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrote _apt helper as _run_cmd that never raises (returns rc/stdout/stderr)
- Handle stderr warnings from apt-cache gracefully (common on Armbian)
- Catch FileNotFoundError if binary missing despite PATH fix
- Show actual backend error message in frontend instead of generic "Search failed"
- Add /api/packages/debug endpoint for troubleshooting apt-cache issues
- Add logging throughout for server-side diagnostics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Package Manager (new app):
- Search, install, remove apt packages via web UI
- Backend: apt-cache/dpkg-query/apt-get wrapper with input validation
- Frontend: searchable package list with expandable detail panels
Text Editor / File Viewer (new app):
- Opens from file manager, supports text editing with line numbers
- Image preview via authenticated blob URLs
- Binary file info display with download option
- Ctrl+S / Cmd+S save, dirty tracking, tab key support
File Manager enhancements:
- Toolbar: New File, New Folder, Upload, Delete, Refresh buttons
- Context menu: New File/Folder options, Open in Editor
- Double-click files to open in editor
- Right-click empty area for create options
Auto-update notification:
- Backend checks git repo for new commits (fetch + compare)
- One-click update: git pull + pip install + service restart
- Toast notification in right panel with dismiss option
- Polls every 30 minutes, retry logic for server restart
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full NetworkManager (nmcli) backed network management:
- Backend: new network.py router with endpoints for device status,
connection config, IPv4 DHCP/static toggle, WiFi scan/connect/disconnect
- Frontend: interactive network settings UI with per-device config,
WiFi network list with signal strength, inline password input
- Graceful 503 fallback to read-only psutil view when nmcli unavailable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add shutil.which guard to _run() in settings, asi_bridge routers
- Catch RuntimeError on WebSocket disconnect in services, asi_bridge
- Make file listing resilient to individual entry errors
- Fix keyboard double-fire on touch devices (touchstart + click)
- Update install.sh with correct Gitea repo URL
- Add six to requirements.txt (python-pam dependency)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full-stack implementation: FastAPI backend with PAM auth, WebSocket
stats/terminal, and vanilla JS frontend with tiling desktop shell.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>