7.7 KiB
7.7 KiB
Goal: migrate the existing razerqdhid app into razer-linux-desktop as a native Linux desktop application using Tauri 2 for the shell and Leptos for the UI.
Context:
- Source app:
./razerqdhid - Target app:
./razer-linux-desktop - Current target state: the target is still the default Tauri + Leptos template (
src/app.rs,src-tauri/src/lib.rs). - Current source stack: Vue 3 + Vite + WebHID + Pyodide/Python.
- Source device/protocol logic lives mainly under
razerqdhid/public/py/. - This project is Linux-only. The app configures supported Razer mice, currently centered on Basilisk V3 / V3 Pro behavior already implemented in the source project.
Local test hardware (dev machine):
- Connected device: Razer Basilisk V3 Pro (USB)
lsusb:1532:00ab(Razer USA, Ltd)- USB sysfs node:
/sys/bus/usb/devices/5-2.2.1 - Exposed HID raw nodes (same VID/PID):
/dev/hidraw3,/dev/hidraw5,/dev/hidraw6ID_USB_INTERFACE_NUM:00->/dev/hidraw3(mouse interface)ID_USB_INTERFACE_NUM:01->/dev/hidraw5ID_USB_INTERFACE_NUM:02->/dev/hidraw6
- Device serial string currently reports as
000000000000(do not rely on it for unique identification).
What exists in the source app:
- A connect flow in
razerqdhid/src/components/ConnectDevice.vue - A main device screen in
razerqdhid/src/components/DeviceMain.vue - Configuration sections for:
- basic settings
- button mapping
- LED
- profiles
- macros
- sensor settings
- device info
- The source app currently relies on browser APIs and Pyodide. The desktop app should replace that with native Tauri/Rust integration.
What I want:
- Port the app into
./razer-linux-desktop. - Keep the migrated app grounded in the existing source behavior and feature structure instead of inventing a new product.
- Replace WebHID/Pyodide with a native backend that talks to the device on Linux.
- Reuse or carefully reimplement the existing protocol logic from the Python code in Rust where appropriate.
- Keep the app runnable after each meaningful step. Do not leave the target in a half-broken state.
Execution requirements:
- Inspect the source app and identify the real feature surface before changing architecture.
- Inspect the Python protocol/device code under
razerqdhid/public/py/and use it as the behavioral reference. - Build the migration incrementally, starting with a working vertical slice instead of attempting every feature at once.
- Preserve the current single-app workflow:
- connect to device
- show main configuration UI
- expose the existing sections progressively
- Prefer Rust/Tauri-native device access over embedding Python in the final architecture unless there is a concrete blocker.
- Keep the UI practical and desktop-oriented. Do not ship the default template UI.
- Do not perform unrelated refactors in the source project unless required for the migration.
Suggested migration order:
- Replace the default Tauri + Leptos starter UI with a real app shell.
- Implement backend device discovery/connection for supported Razer devices on Linux.
- Port the connect screen and basic device info flow.
- Port the basic settings/profile selection path end to end.
- Port the remaining sections in descending order of user value: LED, button mapping, profiles, macros, sensor, info/debug tooling.
- Remove template/demo code once replaced.
Definition of done:
./razer-linux-desktopis the active app, not a starter template.- The app can launch locally as a Tauri desktop app.
- At least one real end-to-end device workflow is implemented natively in the target app.
- The migration status is clear: completed pieces, remaining gaps, and next highest-value steps.
- Any missing source features are called out explicitly instead of being silently dropped.
Faithful Port Checklist (source parity targets):
- App workflow parity:
- Connect screen: scan/select device, connect, error handling, optional "no hardware" mode for UI-only exploration.
- Main screen: profile selector + tabs matching the Vue app: Basic, Button, LED, Profile, Macro, Sensor, Info, plus log/debug tooling.
- Backend protocol parity (map to Python
razerqdhid/public/py/qdrazer/device.py+qdrazer/protocol.py):- Device info:
- manufacturer/product strings (USB indexed strings)
- serial number (
0x0082) - firmware version (
0x0081) - device mode get/set (
0x0084/0x0004) as required for sensor calibration flows
- Basic settings (already partially ported):
- scroll mode get/set (
0x0294/0x0214) - scroll acceleration get/set (
0x0296/0x0216) - smart reel get/set (
0x0297/0x0217) - polling delay get/set (
0x008e/0x000e) - current DPI X/Y get/set (
0x0485/0x0405) - DPI stages get/set (
0x0486/0x0406) and active stage selection
- scroll mode get/set (
- Profile management:
- available profile count/list (
0x0580/0x0581) - create/delete profile (
0x0502/0x0503) - profile info read/write chunking (
0x0588/0x0508) for YAML import/export parity (source exports/imports Basic/Button/LED configs) - flash usage/readiness helpers (
0x068e,0x0086) as needed for safe writes
- available profile count/list (
- LED:
- per-region effect get/set (
0x0f82/0x0f02) including mode/speed/colors - per-region brightness get/set (
0x0f84/0x0f04) - optional static LED write path (
0x0f03) if needed for full parity
- per-region effect get/set (
- Button mapping + Hypershift:
- button function get/set (
0x028c/0x020c) - full
ButtonFunctionencoding/decoding parity withqdrazer.protocol.ButtonFunction- categories used by UI: disabled, mouse, keyboard, macro, dpi_switch, profile_switch, system, consumer, hypershift_toggle, scroll_mode_toggle, plus "custom/raw"
- support both Hypershift OFF and ON mappings
- button function get/set (
- Macros:
- macro list/count (
get_macro_list/get_macro_countpaths in Python) and any required query commands - macro info chunked get/set (
0x068c/0x060c) - macro size get/set (
0x0688/0x0608) - macro function chunked get/set (
0x0689/0x0609) - delete macro (
0x0603) - flash reset tooling (
0x060a+ poll0x068a) for the source "reset flash" workflow - YAML import/export for macro functions (source uses YAML to represent macro op lists)
- macro list/count (
- Sensor / lift-off calibration:
- sensor lift config get/set (
0x0b8b/0x0b0b) - lift config blobs get/set (
0x0b85/0x0b05,0x0b8c/0x0b0c,0x0b8d/0x0b0d) - sensor state + calibration toggles (
0x0b83/0x0b03,0x0b09) - device mode transitions needed by the calibration workflow (
DeviceMode.DRIVER/NORMAL)
- sensor lift config get/set (
- Logs/debug:
- source-equivalent log console: show sent/received report frames, status, and raw bytes
- safe exclusive-access messaging when device replies with "different command" (indicates competing software)
- Device info:
- UI parity notes from Vue components:
- Basic: includes DPI stages editing (count 1..5), active stage, and a "Y = X" helper.
- LED: regions wheel/logo/strip; effects off/static/spectrum/wave variants; per-region speed + brightness; apply-to-all.
- Button: per-button assignments + per-button Hypershift assignments; category editor; custom/raw inspector.
- Profile: create/delete; YAML export/import for selected profile configs (basic/button/led only in source).
- Macro: list macros; load/edit/save/delete; YAML export/import all.
- Sensor: lift config modes; calibration start/stop; parameter calculator + set params.
While working:
- Make concrete code changes, not just a plan.
- Explain architecture decisions briefly when they matter.
- Validate builds/tests when possible.
- If the full migration is too large for one pass, complete the highest-value vertical slice and leave the repo in a clean, runnable state with clear next steps.