iOS remote client: native UIKit surface, WS logging, client-side layout persistence

- Add remote-client-ios: full native UIKit app with Objects/View/UILayout/Functions pattern
- Widget hierarchy: BaseWidget → Fader, Toggle, Transport, Title, Feedback, LogWidget
- LoggingVC: child VC embedded in LogWidget for live WS message inspection
- Codable model: ModelPresetLoadToRemoteDevice with snake_case decoder strategy
- Layout persistence: UserDefaults keyed by preset_uuid, fully client-side
- WS logging: dedicated WS In/Out floating panels on desktop app
- Remove HTTP server and JS client (replaced by native iOS app)
- Add Xcode noise to .gitignore (xcuserstate, xcuserdata)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Paul Lipscomb
2026-07-01 00:02:10 -04:00
parent 4db7165797
commit 7a74cfa75e
38 changed files with 2422 additions and 636 deletions
+4 -1
View File
@@ -9,6 +9,7 @@ class WSServer(QObject):
layout_saved = pyqtSignal(str, object)
client_connected = pyqtSignal()
log_signal = pyqtSignal(str)
raw_in_signal = pyqtSignal(str)
def __init__(self, port=8765, parent=None):
super().__init__(parent)
@@ -37,6 +38,7 @@ class WSServer(QObject):
self.log_signal.emit(f"client disconnected {peer} ({len(self._clients)} remaining)")
def _on_message(self, message):
self.raw_in_signal.emit(message)
try:
data = json.loads(message)
ev = data.get("event")
@@ -64,7 +66,8 @@ class WSServer(QObject):
def broadcast_preset(self, snapshot: dict, layout: dict):
name = snapshot.get("preset_name", "")
self.log_signal.emit(f"broadcast preset '{name}'{len(self._clients)} client(s)")
self.broadcast({"event": "preset", "data": snapshot, "layout": layout})
# layout omitted — owned and persisted client-side, keyed by preset_uuid
self.broadcast({"event": "preset", "data": snapshot})
def broadcast_widget_update(self, uid: str, value: int):
self.broadcast({"event": "widget_update", "uid": uid, "value": value})