Skip to content

Add Avalonia GUI front-end and packaging for CLI + GUI#21

Open
mahoshojoHCG wants to merge 26 commits into
mainfrom
feature/gui
Open

Add Avalonia GUI front-end and packaging for CLI + GUI#21
mahoshojoHCG wants to merge 26 commits into
mainfrom
feature/gui

Conversation

@mahoshojoHCG

@mahoshojoHCG mahoshojoHCG commented Apr 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add Avalonia 12 GUI (qcedl-gui) driving EdlManager directly, with localization (en/zh-Hans/zh-Hant/ja), dark mode, persisted settings, native menus, About + bundled license viewer, and a Claude-inspired theme written from scratch.
  • Extract shared orchestration (EdlManager, EdlOptions, *Runner flows, logging) from QCEDL.CLI into QCEDL.NET so both front-ends share one protocol/runner surface; add non-destructive EnumerateDevices with bus/addr pinning and a device chooser.
  • Add edl-ng-helper + elevation launchers (macOS AppleScript/SMAppService) and Linux udev rules for unprivileged device access; hand-rolled deb/rpm/pacman/dmg packaging for CLI + GUI across platforms.

Test plan

  • dotnet format --verify-no-changes --verbosity diagnostic
  • dotnet build (solution)
  • dotnet run --project QCEDL.GUI/QCEDL.GUI.csproj — connect, switch language/theme, run partitions/sectors/rawprogram/provision flows
  • CLI smoke: printgpt, read-part, rawprogram, provision, reset
  • Publish self-contained CLI + GUI on win-x64 / linux-x64 / osx-arm64
  • Linux: udev rules grant unprivileged access; macOS: helper elevation prompt works

🤖 Generated with Claude Code

mahoshojoHCG and others added 15 commits April 19, 2026 13:03
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces QCEDL.GUI (Avalonia 12 + ReactiveUI) with Overview, Connection,
Partitions, and Logs views driven by a shared EdlService. Refactors the CLI
core so both front-ends can drive EdlManager: extracts EdlOptions as a POCO,
promotes EdlManager / DeviceMode / StorageGeometry to public, and reworks
Logging with an observable sink alongside the console sink. Ships a
Claude-inspired custom theme (tokens + scratch ControlThemes + class
variants), updates CLAUDE.md with the two-front-end architecture, and adds
gui-todos.md as a living tracker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Localized strings (en/zh-Hans/zh-Hant/ja) via a Localizer singleton and
{l:Localize} markup extension, with a Settings screen for runtime language
switching. FontTheme swaps the primary serif/sans/mono families to match
the active CJK script, and GuiSettings persists the chosen culture.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements Phase 1 `read-part` and all Phase 2 read/write/erase flows.
Adds a shared `ConfirmDialog` with optional typed-string gating, a
Sectors view replacing the placeholder, and an operations card on the
Partitions view. Rows in the GPT table auto-populate the op fields on
selection. All new strings are localized across en / zh-Hans / zh-Hant
/ ja.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move rawprogram, dump-rawprogram, and provision orchestration out of the
CLI command files into Core/*Runner static classes, then back the
RawProgram and Advanced GUI views with them (replacing placeholders).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the view-layer Click handlers and StorageProvider/window lookups
with view-model-owned ReactiveCommands and ReactiveUI Interactions, so
VMs no longer depend on Avalonia.Window owners for confirmations or
pickers. Adds ReactiveUI.SourceGenerators to collapse RaiseAndSetIfChanged
boilerplate and command wiring, plus a reflection-based LogCommandErrors
helper on ViewModelBase so unhandled command exceptions always reach the
Logs view. Also adds FindDeviceMacOSSerial so the serial backend
auto-discovers /dev/tty.usbserial-* on macOS instead of requiring
--serial-device.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 4 polish: extend GuiSettings to carry log level + last-used Connection
options (loader, VID/PID, memory, backend) via a shared Current model; surface
a Log-level picker in Settings; wire Window.KeyBindings so nav shortcuts work
on Windows/Linux, not just the macOS NativeMenu; thicken the Focus Blue ring
on buttons and nav items for keyboard discoverability.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EdlManager, EdlOptions, runners, storage backends, and the shared
Logging/Helpers now live in QCEDL.NET so the GUI references only the
protocol library. QCEDL.CLI is now just command wiring, and the GUI's
dependency on QCEDL.CLI is gone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cover EdlManager.EnumerateDevices, DeviceCandidate, EdlOptions.UsbDeviceId,
and the DeviceChooserDialog / RegisterPickDevice bridge so future agents
know the GUI has a session-safe discovery path and a canonical picker
surface for multi-device scenarios.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add `DeviceCandidate` and static `EdlManager.EnumerateDevices(EdlOptions)`
  with non-destructive per-OS backends (Linux sysfs, Windows SetupDi,
  macOS /dev/tty.usbserial-*, libusb).
- Extend `EdlOptions` with `UsbDeviceId` (`usb:vid_XXXX,pid_YYYY,bus_N,
  addr_M`) and teach `QualcommSerial.FindLibUsbDevice` to filter by
  bus+addr so multiple EDL devices can be disambiguated.
- Wire `EdlService.EnumerateDevicesAsync`, a `DeviceChooserDialog` /
  `DeviceChooserDialogViewModel`, and a `DialogBridges.RegisterPickDevice`
  interaction. Connection view gains a Devices card (Scan + list + Clear)
  and auto-prompts from Connect/Probe when >1 candidate matches.
- Fix `EdlService.CloneOptions` silently dropping `Backend`,
  `SerialDevicePath`, and `UsbDeviceId` on session rebuilds.
- Document the auto-prompt behavior and CloneOptions invariant in
  CLAUDE.md; update gui-todos.md tracker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Split semantic brushes in Tokens.axaml into Avalonia ThemeDictionaries so
DynamicResource lookups re-resolve when RequestedThemeVariant changes —
accents and always-on-dark text stay outside the dictionaries. ThemeService
owns the app-wide variant and Settings persists the choice so first paint
matches the user's preference without flicker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ice access

macOS routes every QualcommSerial session through a new edl-ng-helper process
spawned under osascript's administrator-privileges prompt, so the GUI and CLI
no longer need to be launched with sudo. Linux ships a udev rule granting
MODE=0660, GROUP=wheel on Qualcomm EDL USB/tty nodes; users in the wheel group
open the device directly. SMAppService support is stubbed behind the same
launcher interface for future signed-bundle work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rework the CI pipeline to ship both front-ends across every supported RID
and replace dotnet-deb/dotnet-rpm with from-scratch packaging so we own
the file layout, udev-rule placement, and post-install hooks. GUI is
self-contained non-AOT; the macOS helper is AOT/trimmed/single-file and
overwrites the GUI's content copy inside the .app bundle. Signing +
notarization degrade gracefully to ad-hoc when Apple secrets are absent.

Version is now sourced once from Directory.Build.props
(<EdlNgVersion>) — MSBuild inherits <Version> for every csproj and the
workflow greps the same file for package naming. Adds a Debian-variant
udev rule (TAG+="uaccess", GROUP="plugdev") since wheel doesn't exist on
Debian/Ubuntu. Ships a branded icon set (SVG source + rasterized PNG /
ICO / ICNS) wired into the Windows exe, Avalonia window, macOS bundle,
and Linux hicolor theme.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mahoshojoHCG

Copy link
Copy Markdown
Collaborator Author

Overview

image image image image image image image image

Dark Mode

image

Gate the Developer ID import, codesign, and notarize steps on
push-to-main so PRs don't need Apple secrets and still exercise
.app/.dmg packaging. Move the helper's PublishAot/Trimmed/SingleFile
properties into QCEDL.Helper.csproj so they don't propagate as global
MSBuild properties into the analyzer ProjectReference (netstandard2.0),
which trips NETSDK1207 under the .NET 10 SDK on the runner.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Avalonia-based desktop GUI (qcedl-gui) and supporting packaging/elevation infrastructure, while refactoring shared orchestration into QCEDL.NET so both CLI and GUI share the same runner/protocol surface.

Changes:

  • Introduces QCEDL.GUI (Avalonia 12) + QCEDL.Helper (privileged helper) and wires them into the solution/build.
  • Extracts/introduces shared core options + runners + transport elevation plumbing in QCEDL.NET.
  • Adds cross-distro packaging assets (deb/rpm/Arch/Nix, macOS app/dmg), udev rules, icons, and release workflow flattening.

Reviewed changes

Copilot reviewed 143 out of 156 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
resources/udev/99-edl-ng.rules Adds wheel-based udev rule for USB + tty nodes.
resources/udev/99-edl-ng.debian.rules Adds Debian/Ubuntu udev rule variant (uaccess + plugdev).
resources/icons/generate.sh Script to regenerate icon set from the SVG source.
resources/icons/edl-ng.svg Canonical SVG icon source.
resources/icons/edl-ng-16.png Pre-rendered icon asset.
resources/icons/edl-ng-32.png Pre-rendered icon asset.
resources/icons/edl-ng-48.png Pre-rendered icon asset.
resources/icons/edl-ng-64.png Pre-rendered icon asset.
resources/icons/edl-ng-128.png Pre-rendered icon asset.
pkgs/edl-ng/default.nix Installs udev rules in Nix derivation output.
packaging/rpm/qcedl-gui.spec RPM spec for GUI packaging (desktop entry, icons, udev).
packaging/rpm/edl-ng.spec RPM spec for CLI packaging (binary + udev).
packaging/rpm/build-rpm.sh Script to build rpms from prebuilt publish trees.
packaging/macos/build-dmg.sh Script to wrap an app bundle into a dmg.
packaging/macos/build-app.sh Script to assemble a macOS .app from publish output.
packaging/deb/build-gui-deb.sh Script to build GUI .deb (tree install + desktop + icons + udev).
packaging/deb/build-cli-deb.sh Script to build CLI .deb (binary + udev).
packaging/arch/gui/qcedl-gui.desktop Desktop entry for GUI on Linux.
packaging/arch/gui/PKGBUILD Arch PKGBUILD for GUI using CI-staged publish tree.
packaging/arch/cli/PKGBUILD Arch PKGBUILD for CLI using CI-staged publish tree.
edl-ng.slnx Adds GUI + helper projects to solution.
docs/linux-udev.md Documents Linux udev installation/verification for device access.
README.md Updates readme to reflect CLI+GUI, features, and platform notes.
QCEDL.NET/Transport/HelperClient.cs Adds GUI-side client for privileged helper protocol.
QCEDL.NET/Transport/HelperChannel.cs Adds helper frame codec + request/response payload helpers.
QCEDL.NET/Transport/Elevation/MacSmAppServiceLauncher.cs Adds placeholder SMAppService-based launcher (currently unsupported).
QCEDL.NET/Transport/Elevation/MacOsAscriptLauncher.cs Adds osascript-based elevation + Unix socket transport for macOS.
QCEDL.NET/Transport/Elevation/IHelperLauncher.cs Defines helper launcher abstraction and session DTO.
QCEDL.NET/Transport/Elevation/HelperLauncher.cs Chooses best available elevation launcher and resolves helper path.
QCEDL.NET/Transport/Elevation/ElevationPolicy.cs Adds policy/probe logic for when elevation/udev is needed.
QCEDL.NET/QCEDL.NET.csproj Adds InternalsVisibleTo and Vanara dependency for shared code.
QCEDL.NET/Helpers/ProgressReporter.cs Moves helper into shared namespace.
QCEDL.NET/Helpers/Logging.cs Introduces shared logging/event sink for CLI + GUI.
QCEDL.NET/Helpers/ImageSizeParser.cs Moves helper into shared namespace.
QCEDL.NET/Helpers/AlignmentHelper.cs Moves helper into shared namespace.
QCEDL.NET/Core/StorageGeometry.cs Introduces shared storage geometry DTO.
QCEDL.NET/Core/RadxaWoSDeviceManager.cs Moves Radxa WoS backend into shared namespace/deps.
QCEDL.NET/Core/ProvisionRunner.cs Extracts provisioning flow into shared runner.
QCEDL.NET/Core/HostDeviceManager.cs Moves host-device backend into shared namespace; adjusts P/Invoke attributes.
QCEDL.NET/Core/EdlOptions.cs Adds GUI-friendly POCO options surface shared with CLI.
QCEDL.NET/Core/DeviceMode.cs Promotes device mode enum to shared/public.
QCEDL.NET/Core/DeviceCandidate.cs Adds device enumeration candidate model for chooser/pinning.
QCEDL.NET/Core/BlockDeviceManagerBase.cs Moves base class into shared namespace.
QCEDL.Helper/QCEDL.Helper.csproj New privileged helper project (AOT/single-file publish settings).
QCEDL.Helper/Program.cs Helper entrypoint using Unix socket + frame server.
QCEDL.Helper/FrameServer.cs Implements server-side frame dispatch to QualcommSerial + log forwarding.
QCEDL.GUI/macOS/entitlements.plist Adds macOS entitlements for GUI app bundle signing.
QCEDL.GUI/macOS/Info.plist Adds macOS Info.plist template for app bundle.
QCEDL.GUI/app.manifest Adds Windows DPI awareness manifest.
QCEDL.GUI/Views/SettingsView.axaml.cs GUI settings view code-behind.
QCEDL.GUI/Views/SettingsView.axaml GUI settings view layout (language/theme/log-level).
QCEDL.GUI/Views/SectorsView.axaml.cs GUI sectors view code-behind + dialog hookups.
QCEDL.GUI/Views/SectorsView.axaml GUI sectors view layout + progress UI.
QCEDL.GUI/Views/RawProgramView.axaml.cs GUI rawprogram view code-behind + dialog hookups.
QCEDL.GUI/Views/RawProgramView.axaml GUI rawprogram exec/dump view layout.
QCEDL.GUI/Views/PlaceholderView.axaml.cs Placeholder view code-behind.
QCEDL.GUI/Views/PlaceholderView.axaml Placeholder view layout.
QCEDL.GUI/Views/PartitionsView.axaml.cs GUI partitions view code-behind + dialog hookups.
QCEDL.GUI/Views/PartitionsView.axaml GUI partitions view layout + ops panel.
QCEDL.GUI/Views/OverviewView.axaml.cs Overview view code-behind.
QCEDL.GUI/Views/OverviewView.axaml Overview view layout.
QCEDL.GUI/Views/MainWindow.axaml.cs Main window code-behind.
QCEDL.GUI/Views/MainWindow.axaml Shell layout + navigation rail + native menus + keybindings.
QCEDL.GUI/Views/LogsView.axaml.cs Logs view code-behind.
QCEDL.GUI/Views/LogsView.axaml Logs view layout + scrolling log list.
QCEDL.GUI/Views/LicenseViewerDialog.axaml.cs License viewer dialog code-behind.
QCEDL.GUI/Views/LicenseViewerDialog.axaml License viewer dialog layout.
QCEDL.GUI/Views/DeviceChooserDialog.axaml.cs Device chooser dialog logic.
QCEDL.GUI/Views/DeviceChooserDialog.axaml Device chooser dialog layout.
QCEDL.GUI/Views/ConnectionView.axaml.cs Connection view code-behind + dialog hookups.
QCEDL.GUI/Views/ConnectionView.axaml Connection view layout (scan/select/connect + options).
QCEDL.GUI/Views/ConfirmDialog.axaml.cs Confirm dialog logic (typed confirmation + optional link).
QCEDL.GUI/Views/ConfirmDialog.axaml Confirm dialog layout.
QCEDL.GUI/Views/AdvancedView.axaml.cs Advanced view code-behind + dialog hookups.
QCEDL.GUI/Views/AdvancedView.axaml Advanced view layout (provision/upload/reset).
QCEDL.GUI/Views/AboutDialog.axaml.cs About dialog logic + license viewer launch.
QCEDL.GUI/Views/AboutDialog.axaml About dialog layout + dependency list.
QCEDL.GUI/ViewModels/ViewModelBase.cs Adds common VM base with command error logging.
QCEDL.GUI/ViewModels/ShellViewModel.cs Adds shell navigation view model and navigation items.
QCEDL.GUI/ViewModels/SettingsViewModel.cs Settings view model (culture/theme/log persistence).
QCEDL.GUI/ViewModels/OverviewViewModel.cs Overview view model reacting to connection/session state.
QCEDL.GUI/ViewModels/LogsViewModel.cs Logs view model wrapper around observable sink.
QCEDL.GUI/ViewModels/LicenseViewerViewModel.cs License viewer VM.
QCEDL.GUI/ViewModels/DeviceChooserDialogViewModel.cs Device chooser dialog VM.
QCEDL.GUI/ViewModels/ConfirmDialogViewModel.cs Confirm dialog VM with typed confirmation gating.
QCEDL.GUI/ViewModels/AdvancedViewModel.cs Advanced operations VM (provision/upload/reset).
QCEDL.GUI/ViewModels/AboutDialogViewModel.cs About dialog VM (dependency/license metadata).
QCEDL.GUI/Services/ThemeService.cs Central theme switching service (System/Light/Dark).
QCEDL.GUI/Services/ObservableLogSink.cs UI-thread log sink with bounded entry buffer.
QCEDL.GUI/Services/Localizer.cs resx-based localization service with live culture switching.
QCEDL.GUI/Services/LicenseTexts.cs Loads embedded license texts from Avalonia resources.
QCEDL.GUI/Services/GuiSettings.cs JSON-persisted GUI settings (culture/theme/log + connection defaults).
QCEDL.GUI/Services/FontTheme.cs Culture-aware font resource swapping for CJK/Latin.
QCEDL.GUI/Services/EdlService.cs GUI service wrapper over EdlManager with operation serialization.
QCEDL.GUI/Services/DialogInteractions.cs Interaction DTOs + bridges to Avalonia pickers/dialogs.
QCEDL.GUI/Services/AppCommands.cs Global app commands (docs/about) for menus/keybindings.
QCEDL.GUI/QCEDL.GUI.csproj New GUI project definition + packages + embeds licenses + helper content.
QCEDL.GUI/Program.cs Avalonia app bootstrap.
QCEDL.GUI/Markup/LocalizeExtension.cs XAML markup extension for localized strings.
QCEDL.GUI/Markup/AppThemeDisplayConverter.cs Localized display converter for theme enum values.
QCEDL.GUI/Assets/Licenses/vanara.txt Bundled third-party license text.
QCEDL.GUI/Assets/Licenses/reactiveui.txt Bundled third-party license text.
QCEDL.GUI/Assets/Licenses/qcedl-net.txt Bundled third-party license text.
QCEDL.GUI/Assets/Licenses/libusbdotnet.txt Bundled third-party license text.
QCEDL.GUI/Assets/Licenses/inter.txt Bundled third-party license text.
QCEDL.GUI/Assets/Licenses/dotnet-runtime.txt Bundled third-party license text.
QCEDL.GUI/Assets/Licenses/avalonia.txt Bundled third-party license text.
QCEDL.GUI/App.axaml.cs App init: applies persisted culture/theme, wires log sink, creates shell.
QCEDL.GUI/App.axaml App-level styles + native menu entry.
QCEDL.CLI/QCEDL.CLI.csproj Removes per-project version; adds app icon; relies on shared versioning.
QCEDL.CLI/Program.cs Switches CLI helpers usage to shared helper namespace.
QCEDL.CLI/Helpers/Logging.cs Removes CLI-local logging (now shared in QCEDL.NET).
QCEDL.CLI/Helpers/CommandExecutor.cs Imports shared logging.
QCEDL.CLI/Core/StorageGeometry.cs Removes CLI-local DTO (now shared).
QCEDL.CLI/Core/GlobalOptionsBinder.cs Binds global CLI options into shared EdlOptions.
QCEDL.CLI/Commands/WriteSectorCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.CLI/Commands/WritePartitionCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.CLI/Commands/UploadLoaderCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.CLI/Commands/ResetCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.CLI/Commands/ReadSectorCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.CLI/Commands/ReadPartitionCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.CLI/Commands/ProvisionCommand.cs Uses shared ProvisionRunner instead of inline provisioning logic.
QCEDL.CLI/Commands/PrintGptCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.CLI/Commands/EraseSectorCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.CLI/Commands/ErasePartitionCommand.cs Updates command handler signature to shared EdlOptions.
QCEDL.Analyzer/EnumExtensionsGenerator.cs Avoids nested enums; disambiguates hint name with namespace prefix.
Directory.Build.props Centralizes version in EdlNgVersion and sets MSBuild Version.
.github/workflows/release.yml Flattens artifacts under release/ and expands uploaded file set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/linux-udev.md
Comment thread docs/linux-udev.md
Comment thread QCEDL.NET/Transport/HelperChannel.cs Outdated
Comment thread QCEDL.NET/Transport/HelperChannel.cs Outdated
Comment thread QCEDL.NET/Transport/HelperChannel.cs Outdated
Comment thread QCEDL.NET/Transport/HelperChannel.cs Outdated
Comment thread QCEDL.GUI/QCEDL.GUI.csproj
mahoshojoHCG and others added 9 commits April 19, 2026 21:46
…nExclusiveAsync

The privileged helper runs as root, so an Open request with an arbitrary
path would let a compromised GUI coerce it into opening any file. Restrict
the Open path to /dev/{cu,tty}.usb* nodes, reject traversal and symlinks.

Add RunExclusiveAsync overloads that pass the CancellationToken into the
action so GUI runners can actually observe cancellation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the direct-mode branch of ConnectionViewModel.ConnectAsync only
updated status text and returned, so EdlService never constructed an
EdlManager and IsConnected stayed false — leaving Partitions, Sectors,
RawProgram, and Advanced disabled. Force manager construction via a no-op
RunExclusiveAsync so StateChanged fires and IsDirectMode flips IsConnected
to true. USB/Firehose path is untouched.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When a GUI user picked a specific EDL device via the chooser, that pin
was encoded as usb:vid_*,pid_*,bus_N,addr_M. After UploadLoaderViaSahara
the device re-enumerates and typically gets a new bus/addr, so the
bus/addr filter in FindDeviceViaLibUsb failed and the fallback silently
latched onto the first-match VID/PID device — potentially a different
EDL device on the same host.

Capture the USB iSerial descriptor on the initial open (pinned or not)
and prefer it on subsequent FindDeviceViaLibUsb calls. When the captured
iSerial can't be matched and more than one EDL device is visible, refuse
to fall back to first-match and surface a clear error instead. The
single-device happy path is preserved (fallback still works when only
one candidate is present).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The standalone Upload Loader command was gated on WhenConnectedChanged,
which only becomes true once the device has advanced to Firehose/direct
mode — but the command body only succeeds while the device is still in
Sahara (pre-loader). Result: the button was disabled in the only mode
where it works and enabled where it was guaranteed to fail.

Expose IsInSaharaMode + WhenSaharaModeChanged on EdlService (mirroring
IsConnected / WhenConnectedChanged) and gate UploadLoaderCommand on a
dedicated observable combining CanInteract with the Sahara signal.
Provision and Reset keep their existing WhenConnectedChanged gating.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CanExecute gate previously required a discovered candidate, but
EdlManager already honours an explicit SerialDevicePath on EdlOptions.
Widen the predicate to also accept a non-empty manual path (and keep
the direct-mode bypass) so the manual serial-device textbox isn't a
dead end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
macOS exposes some adapters as /dev/tty.USBxxx; the Ordinal prefix check
rejected them before the helper could open the device.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Treat "<?xm" (0x6D783F3C) on the Sahara endpoint after at least one image
has been transferred as "programmer is up" and exit the loop. The prior
code relied on a 1 s read timeout firing before the uploaded Firehose
programmer's XML hello reached the host — a race main happened to win
and current branch loses on macOS IOKit, producing the Unexpected Sahara
command failure after DoneResponse.

Also:
- Route Auto backend to LibUsb on macOS too (not only Windows). The
  IOKit-backed /dev/tty.usbserial-* path needs root; libusb opens the
  USB interface directly without elevation.
- Make ElevationPolicy.RequiresHelper backend-aware so LibUsb sessions
  on macOS bypass the privileged helper.
- Drop the pre-open UsbDevice.TryOpen/Close iSerial read during
  enumeration — perturbed the 9008 device state on macOS and desynced
  the subsequent Sahara handshake. Capture iSerial from the already-open
  libusb handle via a new QualcommSerial.UsbSerialNumber property.
- Store the resolved TransportBackend on EdlManager instead of
  overloading _deviceGuid as a backend flag.
- Wire LibraryLogger.LogAction in the GUI so protocol-layer errors
  (Transfer crash, Pre-transfer validation failed, Unexpected Sahara
  command, etc.) surface in the Logs view instead of being silently
  dropped.
- Document the Auto->LibUsb rule for macOS and the "never pre-open to
  read iSerial" invariant in CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Connection view now shows only a Probe button. Every form field
(loader path, VID/PID, memory type, slot, direct-mode toggles, serial
device path, backend, selected candidate) is merged into a single
observable that projects the options onto EdlService and resets any
live session so the next op re-opens with fresh options.

Downstream VMs (Advanced, Partitions, Raw Program, Sectors) no longer
gate their commands on WhenConnectedChanged / WhenSaharaModeChanged —
EdlService now rebuilds the session on demand from the auto-projected
options, so there is no "must click Connect first" UX.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mahoshojoHCG

Copy link
Copy Markdown
Collaborator Author

Verified E2E working with all GUI features.

macOS access no longer requires a privileged sidecar — LibUsb opens the EDL
interface through IOKit directly. Drops QCEDL.Helper, Transport/Elevation,
HelperClient/HelperChannel, and all packaging / CI wiring that shipped the
helper alongside qcedl-gui.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants