Releases: deeplook/dirplot
v0.4.1
Added
-
--last PERIODfordirplot git— filter commits by a relative time period instead
of (or in addition to)--max-commits. Accepts a number followed by a unit:
m(minutes),h(hours),d(days),w(weeks),mo(months = 30 days).
For GitHub URLs, usesgit clone --shallow-sincefor an efficient date-bounded shallow
clone.--lastand--max-commitsmay be combined (date filter + count cap both apply).dirplot git . -o history.mp4 --animate --last 30d dirplot git . -o history.mp4 --animate --last 24h dirplot git github://owner/repo -o history.mp4 --animate --last 2w --max-commits 10
-
dirplot democommand — new subcommand that runs a curated set of example commands
and saves outputs to a folder. Useful for first-time walkthroughs or verifying that
everything works in a given environment. Accepts--output(default:demo/),
--github-url(default:https://github.com/deeplook/dirplot), and
--interactive/-ito step through and confirm each command individually. Output
uses rich formatting with colour, section rules, and status indicators.dirplot demo # run all examples, save to ./demo/ dirplot demo --output ~/dp-demo --interactive
-
--fade-outfor animated output — appends a fade-out sequence at the end of
animations produced bydirplot git --animate,dirplot watch --animate, and
dirplot replay. Four flags control the effect:--fade-out/--no-fade-out— enable/disable (default: off)--fade-out-duration SECS— total fade length in seconds (default: 1.0)--fade-out-frames N— number of blend steps; defaults to 4 per second of duration
so longer fades are automatically finer-grained--fade-out-color COLOR— target colour:auto(black in dark mode, white in light
mode),transparent(PNG/APNG only; fades to fully transparent), any CSS colour
name, or hex code (e.g."#1a1a2e")
dirplot git . -o history.png --animate --fade-out dirplot git . -o history.mp4 --animate --fade-out --fade-out-duration 2.0 dirplot git . -o history.png --animate --fade-out --fade-out-color transparent
-
--dark/--lightmode for all treemap commands — controls background and border
colours. Dark mode (default) uses a near-black canvas with white directory labels; light
mode uses a white canvas with black labels. Available onmap,git,watch, and
replay.dirplot map . --light dirplot git . -o history.mp4 --animate --light
-
Metadata in MP4/MOV output —
dirplot git,dirplot watch, anddirplot replay
now embed the same dirplot metadata (date, software version, OS, Python version,
executed command) into MP4/MOV files that was previously only written to PNG and SVG.
dirplot read-metareads it back viaffprobe. -
Automatic
ghCLI credential fallback — if--github-tokenandGITHUB_TOKEN
are both absent, dirplot silently runsgh auth token. Users authenticated with the
GitHub CLI (gh auth login) can access private repositories
with no extra configuration. Token resolution order:--github-token→
$GITHUB_TOKEN→gh auth token.
Changed
--fade-out-framesdefaults dynamically toround(fade_out_duration × 4)rather than
a fixed 4, so a 2-second fade automatically uses 8 frames and a 0.5-second fade uses 2.
Fixed
--total-durationovershooting the target length — when many commits fell within
a burst (closely-spaced timestamps), their proportional frame durations would each be
raised to the 200 ms floor, inflating the total well beyond the requested duration
(e.g. 34 s instead of 30 s). The floor is still applied for readability, but the
non-floored frames are now scaled down to compensate so the sum always matches
--total-durationexactly.
Docs
- Added
## dirplot read-metasection todocs/CLI.md(previously undocumented). - Documented external tool requirements:
git(required bydirplot git),ffmpeg
(required for MP4 output),ffprobe(required byread-metaon MP4 files) — in both
README.mdanddocs/CLI.md.
v0.4.0
Added
-
MP4 video output — all three animation commands (
watch --animate,git --animate,
replay) now write MP4 video when the output path ends in.mp4or.mov. Quality is
controlled via--crf(Constant Rate Factor: 0 = lossless, 51 = worst, default 23) and
--codec(libx264H.264 orlibx265H.265). MP4 files are typically 10–100× smaller
than equivalent APNGs. Requiresffmpegon PATH.dirplot git . -o history.mp4 --animate dirplot git . -o history.mp4 --animate --crf 18 --codec libx265 dirplot replay events.jsonl -o replay.mp4 --total-duration 30 dirplot watch . -o treemap.mp4 --animate
-
@refsuffix fordirplot git: local repository paths now accept an optional
@refsuffix to target a specific branch, tag, or commit SHA without needing
--range(e.g.dirplot git .@my-branch -o out.apng --animate).--rangetakes
precedence when both are provided. -
dirplot gitsubcommand — replays a git repository's commit history as an
animated treemap. Each commit becomes one frame; changed tiles receive the same
colour-coded highlight borders aswatch --animate(green = created, blue = modified,
red = deleted). The commit SHA and local timestamp are shown in the root tile header,
and a progress bar at the top of each frame advances as the animation plays.# Animate all commits, write APNG dirplot git . --output history.apng --animate --exclude .git # Last 50 commits on main, 30-second animation with time-proportional frame durations dirplot git . --output history.apng --animate \ --range main~50..main --total-duration 30 # Live-updating static PNG (last frame wins; useful with an auto-refreshing viewer) dirplot git /path/to/repo --output treemap.png --max-commits 100
-
--range(-r): git revision range passed directly togit log
(e.g.main~50..main,v1.0..HEAD). Defaults to the full history of the current branch. -
--max-commits(-n): cap the number of commits processed. -
--frame-duration: fixed frame display time in ms when--total-durationis not set
(default: 1000 ms). -
--total-duration: target total animation length in seconds. Frame durations are
scaled proportionally to the real elapsed time between commits, so quiet periods in
development history map to longer pauses and burst activity to rapid flips. A 200 ms
floor prevents very fast commits from being invisible; durations are capped at 65 535 ms
(APNG uint16 limit). A summary line reports the actual range:
Proportional timing: 200–7553 ms/frame (total ~30.1s). -
--workers(-w): number of parallel render workers in animate mode (default: all
CPU cores). Rendering is memory-bandwidth bound, so 4–8 workers is typically optimal;
use this flag to tune for your hardware. -
Time-proportional progress bar: a 2 px bar at the top of each frame advances in
proportion to animation time consumed, not frame count — so a burst of closely-spaced
commits produces only a small movement while a long quiet period advances it visibly.
With fixed--frame-durationthe bar is linear as before. -
Debounced watch (
--debounce SECONDS, default0.5): thewatchsubcommand now
collects rapid file-system event bursts and regenerates the treemap once per quiet
period instead of on every raw event. Agit checkouttouching 100 files triggers
exactly one render after the activity settles. Pass--debounce 0to restore the
old immediate-fire behaviour.dirplot watch . --output treemap.png # 500 ms debounce (default) dirplot watch . --output treemap.png --debounce 1.0 # 1 s quiet window dirplot watch . --output treemap.png --debounce 0 # immediate, as before
-
Event log (
--event-log FILE): on Ctrl-C exit, all raw file-system events
recorded during the session are written as newline-delimited JSON (JSONL) to the
given file. Each line hastimestamp,type,path, anddest_pathfields.
The log is written only if there are events to record.dirplot watch src --output treemap.png --event-log events.jsonl # Ctrl-C, then: cat events.jsonl | python3 -m json.tool
-
File-change highlights (
--animate): each APNG frame now draws colour-coded
borders around tiles that changed since the previous frame — green for created,
blue for modified, red for deleted, orange for moved. Deleted files are highlighted
retroactively on the previous frame (since the tile no longer exists in the current
one), so the animation clearly shows both the disappearance and the appearance of files.
Moved files appear as a deletion at the old path and a creation at the new path. -
Graceful finalization: Ctrl-C now flushes any pending debounced render before
stopping the observer, so the output file always reflects the final state of the
watched tree. A second Ctrl-C during APNG writing is ignored so the file can finish
being written. -
Tree comment stripping: trailing
# commentsintreeoutput are now ignored
by the path-list parser, so annotated tree listings (e.g.├── config.json # app config)
are parsed correctly. Filenames containing#without a leading space are preserved. -
scripts/apng_frames.py: utility script to list frame durations, dimensions, and
offsets in an APNG file. -
scripts/watch_events.py: utility script to watch directories and log filesystem
events to a CSV file (or stdout) in real time using watchdog. -
--depthforwatch: thewatchsubcommand now accepts--depth Nto limit
recursion depth, matching the behaviour ofdirplot map.dirplot watch . --output treemap.png --depth 3 -
dirplot replaysubcommand — replays a JSONL filesystem event log (as produced
bydirplot watch --event-log) as an animated treemap APNG. Events are grouped into
time buckets (one frame per bucket, default 60 s), with colour-coded highlight borders
matchingwatch --animate. Only files referenced in the event log appear in the
treemap; the common ancestor of all paths is used as the tree root. Frame durations
can be uniform (--frame-duration, default 500 ms) or proportional to the real time
gaps between buckets (--total-duration). Frames are rendered in parallel.# Replay an event log with 60-second buckets, 30-second total animation dirplot replay events.jsonl --output replay.apng --total-duration 30 # Smaller buckets for fine-grained activity, fixed frame duration dirplot replay events.jsonl --output replay.apng --bucket 10 --frame-duration 200
-
dirplot gitaccepts GitHub URLs — pass agithub://owner/repo[@branch]or
https://github.com/owner/repoURL directly todirplot git. dirplot clones the
repository into a temporary directory (shallow when--max-commitsis set, full
otherwise), runs the full history pipeline locally, and removes the clone on exit.
No permanent local copy is created.# Animate the last 50 commits of a GitHub repo — no local clone needed dirplot git github://owner/repo --output history.png --animate --max-commits 50 # Specific branch dirplot git github://owner/repo@main --output history.png --animate --max-commits 50
-
Total commit count shown —
dirplot gitnow reports the total number of commits
available alongside the number being animated, so you can gauge how much history
exists before committing to a longer run:Replaying 20 of 147 commit(s) (increase --max-commits to process more) ...For GitHub URLs the count is fetched with a single cheap API request (one commit
object +Linkheader). For local reposgit rev-list --count HEADis used. -
--github-token($GITHUB_TOKEN): added todirplot gitfor private GitHub
repos or to raise the API rate limit when fetching the total commit count.
Changed
-
libarchive-cis now an optional dependency. Install it with
pip install 'dirplot[libarchive]'(plus the system library:
brew install libarchive/apt install libarchive-dev) to enable
.iso,.cpio,.rpm,.cab,.lha,.xar,.pkg,.dmg,.a,.ar,
and.tar.zst/.tzstsupport. The base install works without it; a clear
error is shown if you try to open one of these formats without the extra. -
--animatewrites the APNG once on exit instead of reading and rewriting the
entire file on every render. Frames are accumulated as raw PNG bytes in memory and
flushed as a single multi-frame APNG when the watcher stops (Ctrl-C). This removes
an O(N²) disk-I/O pattern where frame K required reading a K-frame APNG just to
append one more frame. Status output during a session now readsCaptured frame N;
the finalWrote N-frame APNG → …line confirms the file was written on exit.
Fixed
- Initial scan progress: the
watchsubcommand now printsScanning <roots> …
before the first render and starts the filesystem observer only after the initial
treemap has been generated, avoiding spurious events during the first scan. --animaterace condition: the debounce timer thread was marked as daemon,
causing an in-progress render to be killed when the main thread exited after
observer.join(). The timer is no longer a daemon thread;flush()joins any
in-flight render before stopping.--animatePillow APNG regression: passingpnginfoalongsidesave_all=True
caused Pillow to silently write a static PNG instead of an APNG. Thepnginfo
argument is now omitted from multi-frame saves (cross-process timing metadata is
no longer needed since frames are held in memory for the lifetime of the process).- *APNG frame duration overflow...
v0.3.3
Added
- Breadcrumbs mode (
--breadcrumbs/--no-breadcrumbs,-b/-B, on by default): directories
that form a single-child chain (one subdirectory, no files) are collapsed into a single tile
whose header shows the full path separated by/(e.g.src / dirplot / fonts). When the
label is too wide, middle segments are replaced with…(src / … / fonts). The root tile
is never collapsed. Disable with-Bor--no-breadcrumbs. - Tree depth in root label: the root tile header now includes
depth: Nalongside the
file, directory, and size summary (e.g.myproject — 124 files, 18 dirs, 4.0 MB (…), depth: 6).
The depth reflects the original tree structure and is invariant to whether breadcrumbs mode
is active.
v0.3.2
Added
- Vertical file labels: file tiles that are at least twice as tall as wide now
display their label rotated 90° CCW, letting the text span the full tile height
instead of being squeezed into the narrow width. - Scan and render timing shown in header output:
Found 1,414 files … [2.3s]andSaved dirplot to out.png [0.4s]. - Multiple local roots:
dirplot map src testsaccepts two or more local
directory paths, finds their common parent, and shows only those subtrees. --subtree/-soption (repeatable) — allowlist complement to--exclude:
keep only the named direct children of the root after scanning. Supports nested
paths such as--subtree src/dirplot/fonts.
Fixed
--excludeon pod and Docker backends now prunes entire subtrees — previously only
the exact path was matched, so all children leaked through.- Clearer error for distroless pods: exit code 126 from
kubectl execnow surfaces as
an actionable message explaining that the container has no shell orfindutility. - Adaptive file-label font size is now computed with a single
textbboxmeasurement
(one call per tile) instead of stepping down one pixel at a time — eliminates an
O(font_size × n_tiles) bottleneck that caused near-blocking on large trees such as
.venvdirectories.
Changed
-sshort alias reassigned from--font-sizeto--subtree.--font-sizestill
works as before; it just no longer has a single-letter alias.
v0.3.1
Added
-
github://URI now accepts an optional subpath after the repository name, letting
you scan a subdirectory directly:github://owner/repo/sub/path— subpath on the default branchgithub://owner/repo@ref/sub/path— subpath on a specific branch, tag, or commit SHAhttps://github.com/owner/repo/tree/branch/sub/path— full GitHub URL form- Tags and commit SHAs are supported wherever a branch ref was previously accepted
(e.g.github://torvalds/linux@v6.12), as the GitHub trees API accepts any git ref.
-
--legend Nreplaces the old boolean--legend/--no-legendflag. It now shows a
file-count legend — a sorted list of the top N extensions by number of files,
with a coloured swatch and the file count for each entry:- Pass
--legendalone to use the default of 20 entries. - Pass
--legend 10for a custom limit. - Omit the flag entirely to show no legend.
- The number of rows is also capped automatically so the box never overflows the
image, based on available vertical space and the current--font-size. - Extensions with the same count are sorted alphabetically as a tiebreaker.
- When the total number of extensions exceeds the limit, a
(+N more)line is
appended at the bottom of the box.
- Pass
-
The root tile header now includes a summary of the scanned tree after an em-dash
separator:myproject — 124 files, 18 dirs, 4.0 MB (4,231,680 bytes).
Applies to both PNG and SVG output. The label is truncated with…when the tile
is too narrow to fit the full string. -
Greatly expanded archive format support via the new
libarchive-ccore dependency
(wraps the system libarchive C library):- New formats:
.iso,.cpio,.xar,.pkg,.dmg,.img,.rpm,.cab,
.lha,.lzh,.a,.ar,.tar.zst,.tzst - New ZIP aliases:
.nupkg(NuGet),.vsix(VS Code extension),.ipa(iOS app),
.aab(Android App Bundle) .tar.zst/.tzstrouted through libarchive for consistent behaviour across all
supported Python versions (stdlibtarfileonly gained zstd support in 3.12).libarchive-c>=5.0added as a core dependency alongsidepy7zrandrarfile.
Requires the system libarchive library:
brew install libarchive/apt install libarchive-dev.- See ARCHIVES.md for the full format table, platform notes, and
intentionally unsupported formats (.deb, UDIF.dmg).
- New formats:
-
Encrypted archive handling:
--passwordCLI option passes a passphrase upfront.- If an archive turns out to be encrypted and no password was given, dirplot prompts
interactively (Password:hidden-input prompt) and retries — no need to re-run with a flag. - A wrong password exits cleanly with
Error: incorrect password. PasswordRequiredexception exported fromdirplot.archivesfor programmatic use.- Encryption behaviour by format (since dirplot reads metadata only, never extracts):
- ZIP and 7z: central directory / file list is unencrypted by default → readable without
a password even for encrypted archives. - RAR with header encryption (
-hp): listing is hidden without password;
wrong password raisesPasswordRequired.
- ZIP and 7z: central directory / file list is unencrypted by default → readable without
Fixed
--versionmoved back to the top-leveldirplotcommand (was accidentally scoped
todirplot mapafter the CLI was restructured into subcommands).
v0.3.0
Added
- Kubernetes pod scanning via
pod://pod-name/pathsyntax — useskubectl execand
findto build the tree without copying files out of the pod. Works on any running
pod that has a POSIX shell andfind(GNU or BusyBox). No extra dependency; only
kubectlis required.- Namespace can be specified inline (
pod://pod-name@namespace:/path) or via
--k8s-namespace. - Container can be selected for multi-container pods via
--k8s-container. -xdevis intentionally omitted so mounted volumes (emptyDir, PVC, etc.) within
the scanned path are traversed — the common case in k8s where images declare
VOLUMEentries that are always mounted on a separate filesystem.- Automatically falls back to a portable
sh+statloop on BusyBox/Alpine pods.
- Namespace can be specified inline (
- Docker container scanning via
docker://container:/pathsyntax — usesdocker exec
andfindto build the tree without copying files out of the container. Works on any
running container that has a POSIX shell andfind(GNU or BusyBox). No extra
dependency; only thedockerCLI is required.- Automatically detects BusyBox
find(Alpine-based images) and falls back to a
portablesh+statloop when GNU-printfis unavailable. - Virtual filesystems (
/proc,/sys,/dev) are skipped via-xdev. - Supports
--exclude,--depth,--log, and all other standard options. Dockerfileand.dockerignoreadded so the project itself can be used as a
scan target.
- Automatically detects BusyBox
- SVG output format via
--format svgor by saving to a.svg-suffixed path with--output.
The output is a fully self-contained, interactive SVG file:- CSS hover highlight — file tiles brighten and gain a soft glow; directory headers
brighten on mouse-over (.tile/.dir-tileclasses, no JavaScript needed). - Floating tooltip panel — a JavaScript-driven semi-transparent panel tracks the cursor
and shows the file or directory name, human-readable size, and file-type / item count.
No external scripts or stylesheets — the panel logic is embedded in the SVG itself. - Van Wijk cushion shading — approximated via a single diagonal
linearGradient
overlay (gradientUnits="objectBoundingBox"), defined once and shared across all tiles.
Matches the ×1.20 highlight / ×0.80 shadow range of the PNG renderer.
Disabled with--no-cushion.
- CSS hover highlight — file tiles brighten and gain a soft glow; directory headers
--format png|svgCLI option; format is also auto-detected from the--outputfile
extension.create_treemap_svg()added to the public Python API (from dirplot import create_treemap_svg).drawsvg>=2.4added as a core dependency.- Rename the treemap command to
map(dirplot map ). - Add
termsizesubcommand and restructure CLI as multi-command app. - Add
--dephtparameter to limit the scanning of large file trees. - Support for SSH remote directory scanning (
pip install dirplot[ssh]). - Support for AWS S3 buckets in the cloud (
pip install dirplot[s3]). - Support for local archive files, .zip, tgz, .tar.xz, .rar, .7z, etc.
- Include example archives for 17 different extentions for testing.
- Comprehensive documentation.
github://owner/repo[@branch]URI scheme for GitHub repository scanning. The old
github:owner/reposhorthand has been removed.- File tiles now have a 1-px dark outline (60/255 below fill colour per channel) so
adjacent same-coloured tiles — e.g. a directory full of extension-less files — are
always visually distinct rather than blending into a single flat block.
Changed
docs/REMOTE-ACCESS.mdrenamed todocs/EXAMPLES.md; Docker and Kubernetes pod
sections added; images with captions added for all remote backends.
Fixed
- SVG tooltips now show the original byte count when
--logis active, not the
log-transformed layout value.Node.original_sizeis populated byapply_log_sizes
for both file and directory nodes and is used by the SVG renderer fordata-size. - GitHub error messages are now clear and actionable.
v0.2.0
v0.1.2
v0.1.1
Features
- Nested squarified treemap rendered as a PNG at the exact pixel dimensions of the terminal window.
- Inline terminal display via iTerm2 and Kitty graphics protocols, auto-detected at runtime; supports iTerm2, WezTerm, Warp, Hyper, Kitty, and Ghostty.
- System-viewer fallback (
open/xdg-open) as the default display mode. - File-extension colours from the GitHub Linguist palette (~500 known extensions); unknown extensions fall back to a stable MD5-derived colour from the chosen colormap.
- Van Wijk quadratic cushion shading giving each tile a raised 3-D look (
--cushion, on by default). - Bundled JetBrains Mono fonts for crisp directory labels at any size.
- CLI options:
--output,--show/--no-show,--inline,--legend,--font-size,--colormap,--exclude,--size,--header/--no-header,--cushion/--no-cushion,--log. - Full test suite (65 tests), strict mypy, ruff linting, pre-commit hooks, and CI on Python 3.10–3.13.