Conversation
c575e10 to
200f711
Compare
Right-clicking a drive and choosing "Offload this card" is how an operator expects to reach a tool like this, and the app had no shell presence at all. The verbs live under HKEY_CURRENT_USER\Software\Classes, which is the point of the choice: installing needs no administrator, touches nothing another account can see, and uninstalling is the deletion of two keys. The command line uses %V rather than %1, because a drive root is the case the entry exists for and %1 does not carry it, and it prefers the windowed offloader-gui script so clicking the entry opens the app rather than flashing a console. Finding that script goes through sysconfig rather than guessing beside sys.executable: a --user install puts it under the user scheme, nowhere near the interpreter, which is exactly the layout a first attempt missed. The window now takes an optional source, so the right-clicked path arrives already in the field. Without that the menu entry would be pointless. The icon is drawn, not shipped, for the same reason the PDF's glyphs are: the filmstrip from the report header, rendered to a multi-size .ico with zlib and struct alone. No Pillow, no new dependency, no binary artwork in the repository, and the shell entry, the window and the report header now agree on what this tool looks like. Small sizes simplify rather than shrink, since six sprocket holes and three outlined frames are mush at 16 px. What a registry verb cannot do is appear in the short Windows 11 menu, which is built from packaged COM handlers. The command says so, because a menu entry the operator cannot find is indistinguishable from one that failed to install.
Locating the launcher is the part of this that was actually wrong once: the first version guessed beside sys.executable and missed a --user install, whose entry points live under the user scheme, so it never found the GUI script and fell back to the module without saying so. It only had an assertion that the result was quoted. Now the user scheme is required to be searched, the interpreter's own directory to win over it, duplicates to be absent, and both the preferred-script and fallback paths to be exercised. Uninstall gets the test it needed most: DeleteKey refuses a key with children, so the command subkey has to go first, and the wrong order leaves the verb's parent behind with its command intact. That is worse than not removing it at all, because the menu item survives while installed() keeps answering yes. Re-installing over an existing entry is pinned too, since an upgrade has to repoint the command rather than fail. The icon gains the checks its size-dependent drawing implies: 256 written as zero in the directory, since the field is one byte; the mark staying inside its own canvas at every size, because the canvas silently drops pixels that fall outside and a clipped mark would have no other symptom; the frame count dropping as the tile shrinks, which is the whole claim about simplifying rather than scaling; and a radius larger than the box being clamped.
b0a0a8c to
6930636
Compare
owenpkent
left a comment
There was a problem hiding this comment.
Reviewed per-user shell registration/removal, command quoting, source-path dispatch, icon generation, and the GUI handoff. No actionable defect found in this diff.
The review focused on keeping shell registration scoped to the application's own keys and ensuring the Explorer-selected path reaches the source-selection flow without starting a transfer implicitly. The generated icon is an application asset and does not add a runtime media-tool dependency.
Validation: 49 targeted tests passed on this head and git diff --check passed. Three registry round-trip tests could not run because the sandbox denied HKCU writes; those local checks remain unverified here, although the recorded PR CI checks are green. No interactive Explorer verification was performed.
Summary
Right-clicking a card and choosing "Offload this card" is how an operator expects to reach a tool like this. The app had no shell presence at all.
Adds Offload this card on a drive and Offload this folder on a directory, each opening the app with that path already in the source field.
Decisions worth reviewing
HKEY_CURRENT_USER, notHKEY_CLASSES_ROOT. No administrator, nothing another account can see, and uninstalling is the deletion of two keys.%V, not%1— a drive root is the case this exists for, and%1does not carry it.offloader-guiso the entry opens the app rather than flashing a console. Locating it goes throughsysconfig, not a guess besidesys.executable: a--userinstall puts scripts under the user scheme, nowhere near the interpreter. My first attempt guessed and missed exactly that, falling back topythonw -m.MainWindow(source=...)— without the path arriving pre-filled the menu entry would be pointless.The icon is drawn, not shipped
Same principle as
reports/icons.pydraws the PDF's glyphs: the repository carries no binary artwork.shellicon.pyrenders the report header's filmstrip to a multi-size.icousingzlibandstructalone — PNG encoding and the icon directory by hand. No Pillow, no new dependency (base deps stayxxhashandreportlab).Small sizes simplify rather than shrink: six sprocket holes and three outlined frames are mush at 16 px, so the counts drop and the frames fill. The desktop app now uses the same mark as its window icon, built in memory, so it needs no file on disk to launch.
Honest limitation, surfaced in the tool
A registry verb cannot appear in the short Windows 11 menu — that list is built from packaged
IExplorerCommandCOM handlers. So the entry sits under "Show more options". The install command prints this, because a menu entry the operator cannot find is indistinguishable from one that failed to install.Test plan
tests/test_shell.py— 12 tests: ICO directory vs payload sizes agree, PNG chunk CRCs valid, the mark is actually drawn (not a blank tile passing structural checks), corners transparent, the plan is right (%V, quoted launcher, both classes), off-Windows raises rather than pretendingruffcleanreg queryconfirms both verbs, icon path and command;offloader-gui.exelaunches and stays up with a path argument