v3.2.0: 2.4× faster treemap, 24% less memory, duplicate finder, Recycle Bin - #7
Merged
Merged
Conversation
…export Measured on a 7,300-file tree before changing anything, then again after. Performance: - Hovering the treemap re-rendered the entire map just to move the highlight outline: 242 ms of CPU for every tile the pointer crossed. The outline is a canvas item on top of the rendered image now, so a hover costs about 0.2 ms and the image is untouched. - Treemap render 246 ms -> 103 ms. Two things were behind that. Shading each tile allocated four temporary images and ran two blends and a composite; cushions are now built once per colour at a few resolutions and each tile is a single resize from the nearest one. Tiles under 8px are filled flat, where the gradient cannot be seen anyway. - Pillow re-rounds a list LUT in Python on every call, which showed up as 4.2 million round() calls in the profile; the LUTs are bytes now. - Tile colour is memoised by extension instead of re-deriving the category for every tile. Memory: - Node holds one instance per file on disk. Dropping the dataclass __dict__ for __slots__, and letting files share a single empty children container instead of each allocating a list they can never use, took the tree from 392 to 296 bytes per node (-24%). - Thumbnail requests snap to size buckets, so resizing the window reuses decodes rather than re-decoding every image at its new exact size, and JPEGs are decoded at reduced scale via draft(). Both the thumbnail and cushion caches are now bounded by total pixels rather than entry count, since a few large entries cost far more than many small ones. - The row-preview map is cleared with the rows it points at, and capped. Features: - Duplicates view: finds byte-identical copies by size, then a head/tail sample, then a full hash, so almost nothing is read twice. Shows what keeping one of each would free, runs in the background and is cancellable, and its rows feed the existing zip/delete actions. - Deletions go to the Recycle Bin by default and are undoable; permanent delete is a setting. Falls back automatically when no bin is available. - The treemap can be exported as a PNG alongside the CSV report. Rendering output was compared before and after to confirm the cheaper path looks the same. 133 tests pass, including new coverage for the duplicate finder, the trash helper, and each optimization so it cannot quietly regress. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011HEb3UbLzGGY4KuK54H45Q
CI on main is red on Windows: 103 tests pass and then one errors while building its window, with "Can't find a usable init.tcl". Every GUI test was constructing its own root window, and creating that many Tcl interpreters in a single process fails intermittently on the hosted runner partway through the suite. This change adds more GUI tests, which would have made it more likely to hit, not less. The window is now created once per session and reset between tests: scan target, search, treemap stack, duplicate results, and the resize binding that some tests unbind so they can drive the reflow directly. That takes the suite from 20 root windows to 1, and runs faster for it. The module-level Tk probe goes with it. The session fixture skips the GUI tests when the display cannot be opened, which is what the probe was for. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011HEb3UbLzGGY4KuK54H45Q
A build-only dispatch (no tag) takes the version from the ref name, so on a branch it became "claude/overhaul-standalone-exe-bni8pv" and Compress-Archive failed on the '/' in the destination path. Tag releases were unaffected, which is why it went unnoticed. Strip anything that is not filename-safe before using it, so the build-only path works from any branch. Found by dispatching a build-only run to check the suite on Windows: the tests and both PyInstaller builds succeeded and only the packaging step failed. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011HEb3UbLzGGY4KuK54H45Q
MrHakan
force-pushed
the
claude/overhaul-standalone-exe-bni8pv
branch
from
August 12, 2026 18:23
b0c40f2 to
f199e88
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Optimization pass driven by measurement, plus three functional additions. Everything below was benchmarked on a 7,300-file tree before touching the code and again after.
Performance
The hover number was the real bug. Moving the pointer across a tile boundary re-rendered the entire map just to move the highlight outline. The outline is now a canvas item drawn on top of the cached image, so the image is never rebuilt.
The render win came from profiling, not guessing — and my first attempt actually made it slower (246→493 ms) before the profiler showed why:
round()calls in the profile. The LUTs arebytesnow.Memory
Nodeexists once per file on disk. Swapping the dataclass__dict__for__slots__, and letting files share one empty children container instead of each allocating a list they can never use, cut 24% per node.draft().New features
Testing
Version 3.2.0.
🤖 Generated with Claude Code
https://claude.ai/code/session_011HEb3UbLzGGY4KuK54H45Q
Generated by Claude Code