diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e550a95..1aed936 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,3 +38,27 @@ jobs: - name: Test run: swift test + + # Prose gate: Vale lints the docs against the Microsoft style guide. A style + # violation fails the build, so it never reaches a reviewer. Runs on Linux + # (no Xcode needed) so it starts fast and in parallel with the build matrix. + prose-lint: + name: Docs prose (Vale) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Vale + run: | + VALE_VERSION=3.15.1 + curl -sfL "https://github.com/errata-ai/vale/releases/download/v${VALE_VERSION}/vale_${VALE_VERSION}_Linux_64-bit.tar.gz" -o vale.tar.gz + tar -xzf vale.tar.gz vale + sudo install vale /usr/local/bin/vale + rm -f vale vale.tar.gz + + # Downloads the Microsoft style package declared in .vale.ini. + - name: Sync Vale styles + run: vale sync + + - name: Lint docs prose + run: vale guide docs/adr README.md diff --git a/.gitignore b/.gitignore index 45f6a39..2bd582b 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,9 @@ DerivedData/ # MkDocs build output (built by CI, not committed) /site/ +# Vale prose-lint styles are downloaded by `vale sync` (locally and in CI) +.github/styles/ + # Amore / Sparkle private signing keys — NEVER commit these *.private sparkle_private_key diff --git a/.vale.ini b/.vale.ini new file mode 100644 index 0000000..0228d07 --- /dev/null +++ b/.vale.ini @@ -0,0 +1,19 @@ +# Vale configuration for MacDirStat docs. +# Prose is linted with the Microsoft Writing Style Guide, the same voice the +# guide and ADR were already written in (active voice, present tense, "you"). +StylesPath = .github/styles + +# CI fails on errors (clear-cut style violations). Warnings and suggestions +# still print locally for review but don't block the build. +MinAlertLevel = error + +# Vale downloads the Microsoft package into StylesPath (see the CI workflow and +# the `vale sync` step in the docs). +Packages = Microsoft + +[*.md] +BasedOnStyles = Vale, Microsoft + +# Reference pages quote real API symbols (ByteFormatter, TreemapLayout) and code +# identifiers; don't flag those as spelling errors. +Vale.Spelling = NO diff --git a/README.md b/README.md index 6b4aa8b..98fa50c 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

MacDirStat

See where your disk space went.
-A fast, beautiful macOS disk usage visualizer — built entirely in Swift.

+A fast, beautiful macOS disk usage visualizer, built entirely in Swift.

[![CI](https://github.com/Ti-03/MacDirStat/actions/workflows/ci.yml/badge.svg)](https://github.com/Ti-03/MacDirStat/actions/workflows/ci.yml) [![App Store](https://img.shields.io/badge/Mac_App_Store-Download-0D96F6?style=flat-square&logo=apple)](https://apps.apple.com/app/dirstat/id6766033292?mt=12) @@ -23,21 +23,21 @@ A fast, beautiful macOS disk usage visualizer — built entirely in Swift.

## What it does -MacDirStat scans any folder and turns your filesystem into an interactive sunburst chart — every ring is a depth level, every arc is a file or folder, sized by disk usage. Hover to inspect. Click to drill in. Right-click to delete. +MacDirStat scans any folder and turns your filesystem into an interactive sunburst chart: every ring is a depth level, every arc is a file or folder, sized by disk usage. Hover to inspect. Click to drill in. Right-click to delete. ## Features -- **Sunburst visualization** — depth rings, color-coded by file type (video, code, images, archives…) -- **Spotlight hover** — everything else fades when you hover; selected files pulse with a glow -- **Force Touch haptics** — soft tap for small files, triple thud for multi-GB ones -- **Drill navigation** — click any folder to zoom in, click back to go up -- **Duplicate detection** — finds identical files by content hash, shows wasted space per group -- **One-click cleanup** — keep one copy, trash the rest — or delete file by file -- **File list panel** — sortable tree view beside the chart, toggle to give the chart full width -- **File type breakdown** — top file types with a searchable list of all types -- **Move to Trash** — right-click any arc or row to trash it, chart refreshes instantly -- **CSV export** — dump the full scan as a spreadsheet -- **Settings** — toggle haptic feedback on/off +- **Sunburst visualization**: depth rings, color-coded by file type (video, code, images, archives…) +- **Spotlight hover**: everything else fades when you hover; selected files pulse with a glow +- **Force Touch haptics**: soft tap for small files, triple thud for multi-GB ones +- **Drill navigation**: click any folder to zoom in, click back to go up +- **Duplicate detection**: finds identical files by content hash, shows wasted space per group +- **One-click cleanup**: keep one copy, trash the rest, or delete file by file +- **File list panel**: sortable tree view beside the chart, toggle to give the chart full width +- **File type breakdown**: top file types with a searchable list of all types +- **Move to Trash**: right-click any arc or row to trash it, chart refreshes instantly +- **CSV export**: dump the full scan as a spreadsheet +- **Settings**: toggle haptic feedback on/off ## Screenshots @@ -47,9 +47,9 @@ MacDirStat scans any folder and turns your filesystem into an interactive sunbur ## Install -**[Download on the Mac App Store](https://apps.apple.com/app/dirstat/id6766033292?mt=12)** — published as **DirStat**. +**[Download on the Mac App Store](https://apps.apple.com/app/dirstat/id6766033292?mt=12)**, published as **DirStat**. -Or grab the DMG directly from **[Releases](https://github.com/Ti-03/MacDirStat/releases)** — open it and drag the app to your Applications folder. It updates itself automatically via the Help menu once installed. +Or grab the DMG directly from **[Releases](https://github.com/Ti-03/MacDirStat/releases)**. Open it and drag the app to your Applications folder. It updates itself automatically via the Help menu once installed. **Build from source** @@ -63,14 +63,14 @@ Requires macOS 14+ and Xcode 15+. ## Tech -Pure Swift + SwiftUI — no Electron, no web views, no dependencies. +Pure Swift + SwiftUI: no Electron, no web views, no dependencies. | Layer | What | |---|---| -| Scanner | POSIX `opendir`/`fstatat` with async task groups — parallel, cancellable | +| Scanner | POSIX `opendir`/`fstatat` with async task groups: parallel, cancellable | | Layout | Custom sunburst partition algorithm (band-width from view size) | -| Renderer | SwiftUI `Canvas` — draws 1,000+ arcs at 30 fps | -| Haptics | `NSHapticFeedbackManager` — intensity scales with file size | +| Renderer | SwiftUI `Canvas`: draws 1,000+ arcs at 30 fps | +| Haptics | `NSHapticFeedbackManager`: intensity scales with file size | | Duplicates | SHA-256 content hashing on a background actor | ## Documentation diff --git a/docs/adr/0001-compile-time-guard-for-liquid-glass.md b/docs/adr/0001-compile-time-guard-for-liquid-glass.md index 823a89a..4ef6676 100644 --- a/docs/adr/0001-compile-time-guard-for-liquid-glass.md +++ b/docs/adr/0001-compile-time-guard-for-liquid-glass.md @@ -60,8 +60,8 @@ alone type-checked. The runtime `#available` check stays inside the compile-time branch, so a build made with Xcode 26 still back-deploys correctly to macOS 14/15 at runtime. -We also dropped `macos-14` from the CI matrix: its SDK predates both -`glassEffect` and the `ContentUnavailableView` backport check, so it cannot +I also dropped `macos-14` from the CI matrix: its SDK predates both +`glassEffect` and the `ContentUnavailableView` backport check, so it can't build this project at all regardless of guards, and keeping it would only produce a permanently red, unfixable job. @@ -72,9 +72,9 @@ produce a permanently red, unfixable job. `#available` alone. A future contributor who copies an existing `#available`-only pattern from elsewhere in SwiftUI code will reintroduce this bug; there is no compiler warning for it. -- The CI matrix (`macos-15`, `macos-26`) is now load-bearing: it is the +- The CI matrix (`macos-15`, `macos-26`) is now load-bearing: it's the only thing that would have caught this before it shipped, since a - single-SDK local build cannot reproduce the failure. + single-SDK local build can't reproduce the failure. - Slightly more boilerplate per call site (two nested conditionals instead of one), in exchange for the app actually compiling on the SDK most users' Macs currently have. diff --git a/guide/how-to/read-the-treemap.md b/guide/how-to/read-the-treemap.md index 6ae0ff1..3af5e47 100644 --- a/guide/how-to/read-the-treemap.md +++ b/guide/how-to/read-the-treemap.md @@ -31,9 +31,9 @@ extension: | Pink | Fonts | `ttf`, `otf`, `woff` | | Lime | 3D / models | `obj`, `fbx`, `gltf` | -An extension MacDirStat does not recognize gets a stable color derived from +An extension MacDirStat doesn't recognize gets a stable color derived from its name, so the same unknown extension is always the same color across a -scan. Folders are colored separately from files: each folder gets a muted, +scan. Folders and files use different color schemes: each folder gets a muted, low-saturation tint based on its name, so folders never compete visually with the files inside them. Files also darken slightly with depth, so you can tell a top-level file from one buried five folders down at a glance. @@ -55,7 +55,7 @@ can tell a top-level file from one buried five folders down at a glance. ## See also -- [Scan your first folder](../tutorial/first-scan.md) if you have not run a +- [Scan your first folder](../tutorial/first-scan.md) if you haven't run a scan yet. - [TreemapLayout reference](../reference/treemap-layout.md) for exactly how angles and colors are computed. diff --git a/guide/index.md b/guide/index.md index d384af5..91a1b41 100644 --- a/guide/index.md +++ b/guide/index.md @@ -1,11 +1,11 @@ # MacDirStat -MacDirStat is a native macOS app that scans a folder and shows what is taking up +MacDirStat is a native macOS app that scans a folder and shows what's taking up disk space, as an interactive radial treemap. This guide is for **macOS users who have MacDirStat installed** (or built it from source) and want to scan a folder, read the resulting chart, or look up -what a specific API does. You do not need any Swift experience to follow the +what a specific API does. You don't need any Swift experience to follow the tutorial or how-to guide; the reference section assumes you are reading the Swift source. diff --git a/guide/reference/treemap-layout.md b/guide/reference/treemap-layout.md index e3c633f..3aef8e1 100644 --- a/guide/reference/treemap-layout.md +++ b/guide/reference/treemap-layout.md @@ -40,7 +40,7 @@ let arcAngle = fraction * totalAngle An arc smaller than `minArcAngle` (about 1 degree) is skipped entirely rather than drawn as an unreadable sliver. Folders recurse into their own -children at the next radius band; files do not. +children at the next radius band; files don't. ## `TreemapCell` @@ -61,9 +61,9 @@ One cell per arc. `startAngle`/`endAngle` are in radians; `innerRadius`/ ## Color assignment -Colors are not assigned by `TreemapLayout` directly; it delegates to +Colors aren't assigned by `TreemapLayout` directly; it delegates to `ExtensionColorMap.color(for:)` for files and computes a muted per-folder tint by hashing the folder's name for directories. Files also darken by a fixed amount per depth (from 0% at depth 0 up to 38% at depth 5+), so a `.mp4` five folders deep is visibly darker than one at the top level, even -though both are "orange". +though both are "orange." diff --git a/guide/tutorial/first-scan.md b/guide/tutorial/first-scan.md index 2b342f4..e28d020 100644 --- a/guide/tutorial/first-scan.md +++ b/guide/tutorial/first-scan.md @@ -34,6 +34,6 @@ proportional to how much disk space it uses. ## Next step -The colors are not random. Continue to +The colors aren't random. Continue to [How to read the treemap](../how-to/read-the-treemap.md) to learn what each color means and how to change the color scheme.