Consolidates everything the desktop app depends on into app-desktop/ so it
packages cleanly as a standalone Windows exe:
- app-desktop-macos/ -> app-desktop/src/ (drop macOS-only naming, app now
targets Windows via PyInstaller too)
- remote-server/ws_server.py and app-presets/ moved into app-desktop/src/
(both were exclusive dependencies of main.py/presets.py) — fixes a
PyInstaller "missing module" error for ws_server that only surfaced once
packaging was attempted, since its old location wasn't on the analyzer's
search path
- .venv relocated into app-desktop/src/ alongside the code it serves
- run.py moved into src/, path logic simplified now that it's co-located
with main.py instead of bridging from the repo root
- Deleted app/, server/ — stale __pycache__-only fossils from prior reorgs
- .gitignore: added missing .venv/ entry (was only covering venv/, a
different name — the real folder was never actually ignored) and
app-desktop/build|dist/ for PyInstaller output
presets.py: added a frozen-vs-source branch. Running from source is
unchanged (app-presets/ next to the code). A packaged exe can't write to
Program Files, so it uses %APPDATA%\VirtualController\ instead — standard
Windows convention. First launch on a machine with no AppData presets yet
seeds from presets.json bundled into the exe (PyInstaller --add-data,
baked into VirtualController.spec), so a fresh install starts with real
presets instead of empty; every launch after that only touches AppData.
Verified: exe builds clean (no missing-module warnings), launches with a
real window, and a clean first-launch correctly seeds AppData from the
bundled presets.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bisect session with per-suspect kill switches (see BISECT_LOG.md):
- appendLog was the confirmed stutter culprit — ran per-message (~110/sec)
on the main thread even with the log panel hidden; A/B verified. Disabled
via DEBUG_DISABLE_APPEND_LOG; shippable visible-only fix still TODO.
- Per-message Log.d also disabled (freebie, no felt difference).
- Glow, gradient fill, network flash, feedback setText all exonerated and
restored; switches left in place at false.
- FocusFeedbackWidgetView rewritten: TextView -> bare View + canvas.drawText.
60hz updates are now field-assign + invalidate, no per-update text Layout.
- ContextMenuViewLogic: explicit color-picker branch for the new view type.
- Timecode chunkiness root cause was REAPER audio buffer size (source-side
burst cadence), documented in PERF_SESSION_2026-07-31.md; stutter bug
report stamped RESOLVED.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Performance issue where high-frequency network updates (60hz timecode) cause
both feedback widget and fader touch input to stutter. Works fine on iOS and
desktop at same update rate. Detailed analysis and investigation steps included.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Button styling standardization with green ring state indicators (Connect, Rescan, Log, Clear Layout, Reset ALL)
- Input field labels with proper DP-based padding and spacing
- Feedback & title widget color picker with 7-color palette and persistence layer
- Font weight selection for text widgets (deferred visual implementation)
- Hide/Show and Reset Size buttons in context menu
- Flashing pink border around entire app when Arrange mode is ON (500ms fade in/out cycle)
- Arrange button disabled until connected to desktop app, with alpha indicator
- WidgetLayoutState now persists color and fontWeight properties
- Arrange mode visual debugging with smooth fade animation instead of harsh strobe
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Auto-memory is normally stored outside the repo, keyed to the working
directory's absolute path, so it doesn't follow a clone to a new machine.
This snapshot travels with the repo instead, so Claude has the same
project/feedback context when opened from a different machine (e.g. the
Windows VM being set up for the extension-reaper-macos port).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Splits socket/tracking into their own subdirectories and pulls the hello
action registration out into its own module, matching the one-module-per-
folder pattern; build.sh now outputs into build/macos instead of the repo root.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
extension-reaper-macos: split main.cpp into tracking.cpp (polls selected
envelope/automation items, reads/writes D_BASELINE) and socket.cpp (basic
UDP listener, non-blocking, drains all pending packets per tick). Nudging
now applies to every selected automation item across every track in the
project, not just the first one found.
app-desktop-macos: new "Extension" column (mirrors the OSC/Remote columns)
to enable/disable the UDP connection and point it at a host/port, plus a
log window. FocusFaderWidget gets an OSC/Ext output-mode toggle (mirrors
TransportWidget's MIDI/OSC toggle) so a fader can drive the extension
directly instead of/alongside OSC. Also renamed the Remote/Tablet buttons
for clarity and split their status indicator into its own column.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
C/C++ extension built directly against the vendored Cockos reaper-sdk
headers (no Rust/reaper-rs wrapper layer). Loads, registers a test
action, and confirms via REAPER's console — confirmed working in
REAPER 2026-07-15.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Stripped upstream git history; starting point for replacing the native
SWELL/Win32 mapping UI with something more suited to bulk editing.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Desktop:
- New dedicated MIDI Controller In port (separate from MIDI In), same CC learn/bind pipeline but never passes notes through; positioned first in the IO column order
- Fixed MIDI In "Not Connected" not persisting across restart (missing save_io_config call)
- Fixed MIDI Out/Transport Out mutual-exclusion incorrectly flagging "Not Connected" as an in-use port
- Renamed "Remote" checkbox to "App" on Fader/Toggle/Transport widgets
- FocusFaderWidget/FocusToggleWidget: new Hardware section (Learn + CC/CH bind) so a physical controller can drive a Channel Focus widget alongside the app/OSC, plus an independent Feedback checkbox that sends translated CC out (for motorized fader / LED sync)
- JL Cooper touch-sense profile ("No Profile" / "MIDI JL Cooper CC Mode") on FaderWidget/FocusFaderWidget: touch channel (value channel - 1, same CC) is filtered out of the value and gates incoming OSC/DAW feedback while touching
- FocusFeedbackWidget: "Custom" checkbox to show a manually-typed static string instead of live OSC feedback, disabling Learn/OSC input and suppressing the iPad broadcast while active
- New daw-config-reaper/ folder with reaper-osc-config-paul-custom.ReaperOSC
iOS:
- New FocusToggleWidget (dedicated Channel Focus toggle, split out of shared ToggleWidget) with colorName-aware darken/border and isMomentary handling — keeps Channel-Focus-only behavior off the regular ToggleWidget
- Arrange mode long-press menu: text alignment (left/center/right) and a 10-color named-palette Text Color picker for TitleWidget/FocusFeedbackWidget, persisted in the saved layout
- FocusFeedbackWidget: Reset Size action, monospaced-digit font fix for timestamp/counter jitter
- New "Cancel Changes" button in Arrange mode — reverts layout/hides/aligns to the state at Arrange-mode entry and restores desktop-side Remote/dest assignments
- Grid line brightness increased for visibility
- Bonjour discovery for desktop auto-connect
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Multi-select strips with click, Ctrl+click (toggle), Shift+click (range, same zone)
- Right-click selected strip: context menu with Cascade CC, Assign Channel, Deselect All
- Right-click unselected strip: clears all selections
- Selection status label shows count and type (faders/toggles/transport toggles)
- Rename preset button added to preset row
- Duplicate preset button auto-generates new UUID
- Layout panel section buttons show strikethrough when hidden, normal when visible
- Renamed Preset Browser to Instrument Browser in UI
- Transport strips wired for multi-select and context menu
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wire all 5 Layout panel section toggles (Preset Browser, Big Title, Fader Row, Toggle Row, Transport Row)
- Replace checkboxes with clickable title buttons (highlighted=visible, dimmed=hidden)
- Move Big Title inside fader container, between Hide Params button and fader strips
- Wrap transport/fader/toggle rows in container widgets for setVisible support
- Reposition separator between preset browser and mixer row
- Open Layout panel by default on launch
- Add Duplicate preset button (auto-generates new UUID)
- Add Rename preset button
- Swap preset browser label and show-hide button order
- Persist section visibility state per preset under new 'sections' key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ToggleWidget: add Momentary/Toggle mode selector (mirrors TransportWidget
minus OSC); HW routing respects trigger_mode; fix invisible button on init
by removing explicit empty stylesheet; saves/loads trigger_mode per preset
- Big Title: full-width 28px bold QLineEdit above fader row, saves per-preset
as big_title; green underline on focus; single bottom border, no separator
- Layout panel: floating 220px window to the left of main window, toggled by
new Layout button next to Messages; shows Sections checkboxes (Preset
Browser, Big Title, Fader Row, Toggle Row, Transport Row); tracks window
move/resize; checkboxes not yet wired to functionality
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- transport widget restructured to two-group lasso layout (grp_learn + grp_cc) matching fader/toggle pattern
- fixed MIDI learn for transport (bind_hw_cc channel param) and preset browser (wired to check_and_start_midi_learn)
- transport HW routing now respects output_mode and trigger_mode; OSC mode fires on any value, MIDI momentary fires on any value
- preset browser on_hw_trigger fires on any value (0 or 127 both send 127 out)
- unbound CCs are dropped, no passthrough; notes (0x80/0x90) pass through freely to midi_out
- four MIDI/OSC log windows: OSC In, OSC Out, MIDI In, MIDI Out with formatted note/CC display
- midi_out_signal now carries message list for logging
- lock preset checkbox: always defaults to True on launch, not persisted
- transport cc_output_lbl always visible, content adapts to MIDI/OSC mode
- preset browser show/hide saves and restores correctly per preset
- CLAUDE.md added with full project architecture, conventions, and planned features
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Extract all QWidget/QLayout instances and property configuration to module level
- Group by element: constructor → properties for each widget
- Keep __init__ assembly-only: addWidget, addLayout, addStretch, signals
- Explicit naming for clarity: separator_vertical_fader_zone_left, etc
- Applies to: IO columns (MIDI IN/OUT, Transport, OSC), Preset Browser, Transport/Fader/Toggle rows, OSC feedback containers
- Pattern: Module = what it is; __init__ = where it goes