fix pango_layout_get_pixel_size args
- SHA
861b8c12898fe697e4fd269b316ac8a046282436- Parents
-
6a5d00a - Tree
40c65c9
861b8c1
861b8c12898fe697e4fd269b316ac8a0462824366a5d00a
40c65c9BETTER-PROPERTIES-BUTTON.mdadded@@ -0,0 +1,13 @@ | ||
| 1 | +# improving the proerties button in sniffly | |
| 2 | + | |
| 3 | +right now this button just displays status text in the bottom | |
| 4 | + | |
| 5 | +I propose we make that status text the default when any selection (not hover) is made | |
| 6 | +that is to say the current properties status indicator we added should be there whenever we have an active selection for that selection | |
| 7 | + | |
| 8 | + so if i open to downloads and then select inMusic.app or whatever the dir is, I expect to see the status info at the bottom left by defaultt, and change if i select another file and disappear if i deselct. | |
| 9 | + | |
| 10 | +I propose the properties button opens the finder/mac os system properties viewer since we seem to be leaning towards a mac os exlusive buid here | |
| 11 | +what're you're thoughts on the new behavior of the button and implementing the fixes to status behavior (always on selection)) | |
| 12 | + | |
| 13 | + | |
BUGFIX_SESSION.mdadded@@ -0,0 +1,64 @@ | ||
| 1 | +# Bug Fix Session - UI Issues | |
| 2 | + | |
| 3 | +## Issues to Fix | |
| 4 | + | |
| 5 | +### 1. Eye Button (Toggle Dotfiles) Not Working | |
| 6 | +**Status**: ✅ FIXED | |
| 7 | +**Description**: Clicking the eye button to toggle dotfiles visibility doesn't affect rendering. Testing in a project with `.git` - the `.git` directory doesn't appear/disappear when toggled. | |
| 8 | + | |
| 9 | +**Expected Behavior**: | |
| 10 | +- Eye button ON → show `.git`, `.cache`, `.config`, etc. | |
| 11 | +- Eye button OFF → hide all dotfiles | |
| 12 | + | |
| 13 | +**Files Modified**: | |
| 14 | +- `src/core/progressive_scanner.f90` - Added `show_hidden_files` flag and filtering logic | |
| 15 | +- `src/rendering/treemap_renderer.f90` - Updated `toggle_hidden_files()` to call both scanner modules | |
| 16 | + | |
| 17 | +**Solution**: Added hidden file filtering to progressive scanner at all depth levels (depth 0 and depth > 0). The toggle now properly filters files starting with '.' during scanning. | |
| 18 | + | |
| 19 | +--- | |
| 20 | + | |
| 21 | +### 2. Collapsed Path in File Selector | |
| 22 | +**Status**: ✅ FIXED | |
| 23 | +**Description**: When opening with `./build/sniffly .` the file selector shows `.` instead of the full absolute path. | |
| 24 | + | |
| 25 | +**Expected Behavior**: | |
| 26 | +- Command: `./build/sniffly .` | |
| 27 | +- File selector should show: `/Users/matthewwolffe/Documents/GithubOrgs/FortranGoingOnForty/sniffly` | |
| 28 | +- Not: `.` | |
| 29 | + | |
| 30 | +**Files Modified**: | |
| 31 | +- `app/main.f90` - Added `get_absolute_path()` call to expand relative paths | |
| 32 | + | |
| 33 | +**Solution**: Modified main.f90 to call `get_absolute_path()` on the command-line argument before passing it to `sniffly_set_scan_path()`. This expands `.` to the full absolute path. | |
| 34 | + | |
| 35 | +--- | |
| 36 | + | |
| 37 | +### 3. Toggle Flat/3D Rendering Mode Does Nothing | |
| 38 | +**Status**: ✅ FIXED | |
| 39 | +**Description**: The toggle flat/3D rendering button doesn't appear to change the visualization at all. | |
| 40 | + | |
| 41 | +**Expected Behavior**: | |
| 42 | +- Flat mode → Simple colored rectangles | |
| 43 | +- 3D/Cushion mode → Shaded rectangles with depth effect | |
| 44 | + | |
| 45 | +**Files Modified**: | |
| 46 | +- `src/rendering/treemap_renderer.f90` - Enhanced cushion shading parameters for more visible 3D effect | |
| 47 | + | |
| 48 | +**Solution**: The toggle was working correctly, but the 3D cushion effect was too subtle to notice. Increased the ridge height factor from 0.5 to 2.0 and adjusted lighting (ambient from 0.4 to 0.3, diffuse from 0.6 to 0.7) to make the 3D effect much more pronounced and visually obvious. | |
| 49 | + | |
| 50 | +--- | |
| 51 | + | |
| 52 | +## Progress Tracking | |
| 53 | + | |
| 54 | +- [x] Issue 1: Fix dotfiles toggle ✅ | |
| 55 | +- [x] Issue 2: Fix collapsed path display ✅ | |
| 56 | +- [x] Issue 3: Fix flat/3D rendering toggle ✅ | |
| 57 | + | |
| 58 | +## Summary | |
| 59 | + | |
| 60 | +All three UI issues have been fixed: | |
| 61 | + | |
| 62 | +1. **Eye button** - Now properly filters dotfiles in progressive scanner | |
| 63 | +2. **Path display** - Now shows full absolute paths instead of relative paths | |
| 64 | +3. **Render mode toggle** - Enhanced cushion shading for clearly visible 3D effect | |
Casks/sniffly.rbadded@@ -0,0 +1,16 @@ | ||
| 1 | +cask "sniffly" do | |
| 2 | + version "0.2.4" | |
| 3 | + sha256 :no_check # Will calculate actual SHA256 once DMG is hosted | |
| 4 | + | |
| 5 | + url "https://github.com/FortranGoingOnForty/sniffly/releases/download/v#{version}/Sniffly-#{version}-macOS.dmg" | |
| 6 | + name "Sniffly" | |
| 7 | + desc "Fast, visual disk space analyzer built with Fortran and GTK4" | |
| 8 | + homepage "https://github.com/FortranGoingOnForty/sniffly" | |
| 9 | + | |
| 10 | + # No dependencies needed - all GTK4 libraries are bundled! | |
| 11 | + app "Sniffly.app" | |
| 12 | + | |
| 13 | + zap trash: [ | |
| 14 | + "~/Library/Logs/Sniffly.log", | |
| 15 | + ] | |
| 16 | +end | |
DYNAMIC_RENDERING_PLAN.mdadded@@ -0,0 +1,290 @@ | ||
| 1 | +# Dynamic Rendering Plan - SpaceSniffer-Style Progressive Scanning | |
| 2 | + | |
| 3 | +## Goal | |
| 4 | +Implement real-time progressive rendering where treemap boxes grow, shrink, and reposition as directories are scanned, similar to SpaceSniffer's iconic behavior. | |
| 5 | + | |
| 6 | +## Current State | |
| 7 | +- **Synchronous scanning**: Full directory tree is scanned before any rendering | |
| 8 | +- **Static layout**: Once rendered, boxes don't change size | |
| 9 | +- **Progress bar only**: User sees only a progress bar during scan, no visual feedback of structure | |
| 10 | + | |
| 11 | +## Target Behavior (SpaceSniffer-like) | |
| 12 | +1. **Immediate rendering**: Start drawing boxes as soon as first directory is scanned | |
| 13 | +2. **Progressive updates**: Boxes dynamically resize as subdirectories are discovered | |
| 14 | +3. **Relative sizing**: Boxes continuously adjust relative to siblings as scan progresses | |
| 15 | +4. **Visual feedback**: User sees the file structure emerge in real-time | |
| 16 | +5. **Smooth experience**: No jarring jumps, boxes flow and adapt gracefully | |
| 17 | + | |
| 18 | +## Technical Challenges | |
| 19 | + | |
| 20 | +### 1. **Asynchronous Scanning** | |
| 21 | +- **Current**: Recursive `scan_directory()` blocks until complete | |
| 22 | +- **Needed**: Non-blocking scan that yields partial results | |
| 23 | +- **Options**: | |
| 24 | + - Thread-based: Scan in background thread, communicate via shared state | |
| 25 | + - Idle callback: Scan one directory per GTK idle iteration | |
| 26 | + - Generator pattern: Yield after each directory scan | |
| 27 | + | |
| 28 | +### 2. **Incremental Layout Updates** | |
| 29 | +- **Current**: `squarify_layout()` computes entire layout at once | |
| 30 | +- **Needed**: Re-layout on every size update | |
| 31 | +- **Performance**: Must be fast enough for 30-60 FPS during scan | |
| 32 | +- **Solution approaches**: | |
| 33 | + - Cached layouts with partial invalidation | |
| 34 | + - Incremental squarify (update only affected nodes) | |
| 35 | + - Layout diffing to minimize recalculation | |
| 36 | + | |
| 37 | +### 3. **Size Estimation** | |
| 38 | +- **Problem**: When scanning `/foo`, we don't know final size until all children scanned | |
| 39 | +- **SpaceSniffer approach**: Use "estimated" sizes that grow as scan progresses | |
| 40 | +- **Implementation**: | |
| 41 | + - Track `scanned_size` vs `estimated_total_size` per node | |
| 42 | + - Mark nodes as `scan_complete` flag | |
| 43 | + - Use heuristics for estimation (e.g., average file size × file count estimate) | |
| 44 | + | |
| 45 | +### 4. **Treemap Algorithm Modifications** | |
| 46 | +- **Standard squarify**: Assumes all sizes are final | |
| 47 | +- **Dynamic squarify**: Must handle changing sizes gracefully | |
| 48 | +- **Requirements**: | |
| 49 | + - Stable positioning (boxes shouldn't jump around too much) | |
| 50 | + - Smooth transitions (animate size changes) | |
| 51 | + - Handle new children appearing mid-render | |
| 52 | + | |
| 53 | +### 5. **Threading & GTK Integration** | |
| 54 | +- **GTK is not thread-safe**: All widget updates must be on main thread | |
| 55 | +- **Architecture**: | |
| 56 | + ``` | |
| 57 | + Main Thread (GTK) Background Thread (Scanner) | |
| 58 | + ┌──────────────┐ ┌────────────────┐ | |
| 59 | + │ Render loop │◄──Messages────│ scan_directory │ | |
| 60 | + │ (60 FPS) │ │ (breadth-first)│ | |
| 61 | + │ │───Request────►│ │ | |
| 62 | + │ Layout calc │ │ File I/O │ | |
| 63 | + └──────────────┘ └────────────────┘ | |
| 64 | + ``` | |
| 65 | + | |
| 66 | +### 6. **State Synchronization** | |
| 67 | +- **Problem**: Scan thread modifies tree while render thread reads it | |
| 68 | +- **Solution options**: | |
| 69 | + - Double buffering: Scan writes to buffer, swap atomically | |
| 70 | + - Read-write locks: Readers don't block each other | |
| 71 | + - Message passing: Scan sends deltas, render applies them | |
| 72 | + | |
| 73 | +## Proposed Architecture | |
| 74 | + | |
| 75 | +### Phase 1: Non-Blocking Scan (Idle Callback Approach) | |
| 76 | +**Simplest, no threading** | |
| 77 | + | |
| 78 | +```fortran | |
| 79 | +module progressive_scanner | |
| 80 | + type :: scan_state | |
| 81 | + character(len=512) :: current_path | |
| 82 | + integer :: depth | |
| 83 | + type(file_node), pointer :: current_node | |
| 84 | + logical :: complete | |
| 85 | + ! Queue of directories to scan | |
| 86 | + character(len=512), dimension(1000) :: pending_dirs | |
| 87 | + integer :: queue_head, queue_tail | |
| 88 | + end type scan_state | |
| 89 | + | |
| 90 | + type(scan_state), save :: scanner | |
| 91 | + | |
| 92 | +contains | |
| 93 | + | |
| 94 | + ! Initialize scan | |
| 95 | + subroutine start_progressive_scan(root_path) | |
| 96 | + scanner%queue_head = 1 | |
| 97 | + scanner%queue_tail = 1 | |
| 98 | + scanner%pending_dirs(1) = root_path | |
| 99 | + scanner%complete = .false. | |
| 100 | + | |
| 101 | + ! Register idle callback | |
| 102 | + call g_idle_add(c_funloc(scan_one_directory), c_null_ptr) | |
| 103 | + end subroutine | |
| 104 | + | |
| 105 | + ! Scan one directory per idle iteration | |
| 106 | + function scan_one_directory(user_data) bind(c) result(continue) | |
| 107 | + type(c_ptr), value :: user_data | |
| 108 | + integer(c_int) :: continue | |
| 109 | + | |
| 110 | + if (scanner%queue_head > scanner%queue_tail) then | |
| 111 | + scanner%complete = .true. | |
| 112 | + continue = 0_c_int ! Stop calling | |
| 113 | + return | |
| 114 | + end if | |
| 115 | + | |
| 116 | + ! Scan one directory | |
| 117 | + call scan_single_directory(scanner%pending_dirs(scanner%queue_head)) | |
| 118 | + scanner%queue_head = scanner%queue_head + 1 | |
| 119 | + | |
| 120 | + ! Request redraw | |
| 121 | + call gtk_widget_queue_draw(widget_ptr) | |
| 122 | + | |
| 123 | + continue = 1_c_int ! Keep calling | |
| 124 | + end function | |
| 125 | + | |
| 126 | +end module progressive_scanner | |
| 127 | +``` | |
| 128 | + | |
| 129 | +**Pros**: | |
| 130 | +- Simple, no threading complexity | |
| 131 | +- GTK-friendly | |
| 132 | +- Easy to debug | |
| 133 | + | |
| 134 | +**Cons**: | |
| 135 | +- Slower than threaded (I/O blocks GUI) | |
| 136 | +- May feel sluggish on slow disks | |
| 137 | + | |
| 138 | +### Phase 2: Background Thread Scan | |
| 139 | +**Better performance, more complex** | |
| 140 | + | |
| 141 | +```fortran | |
| 142 | +module threaded_scanner | |
| 143 | + use omp_lib | |
| 144 | + | |
| 145 | + type :: scan_message | |
| 146 | + integer :: msg_type ! 1=new_node, 2=update_size, 3=complete | |
| 147 | + character(len=512) :: path | |
| 148 | + integer(int64) :: size | |
| 149 | + integer :: parent_id, node_id | |
| 150 | + end type | |
| 151 | + | |
| 152 | + ! Thread-safe message queue | |
| 153 | + type(scan_message), dimension(10000) :: message_queue | |
| 154 | + integer :: queue_read_pos = 1, queue_write_pos = 1 | |
| 155 | + !$omp threadprivate(queue_read_pos, queue_write_pos) | |
| 156 | + | |
| 157 | +contains | |
| 158 | + | |
| 159 | + subroutine start_background_scan(root_path) | |
| 160 | + !$omp parallel | |
| 161 | + !$omp single | |
| 162 | + call scan_directory_async(root_path) | |
| 163 | + !$omp end single | |
| 164 | + !$omp end parallel | |
| 165 | + | |
| 166 | + ! Register timer to process messages on main thread | |
| 167 | + call g_timeout_add(16_c_int, c_funloc(process_scan_messages), c_null_ptr) | |
| 168 | + end subroutine | |
| 169 | + | |
| 170 | + recursive subroutine scan_directory_async(path) | |
| 171 | + ! Scan directory, send messages for each discovery | |
| 172 | + ! ... | |
| 173 | + call send_message(NEW_NODE, path, size, parent_id) | |
| 174 | + !$omp task | |
| 175 | + call scan_directory_async(child_path) | |
| 176 | + !$omp end task | |
| 177 | + end subroutine | |
| 178 | + | |
| 179 | + function process_scan_messages(user_data) bind(c) result(continue) | |
| 180 | + ! Process up to 100 messages per frame | |
| 181 | + do i = 1, 100 | |
| 182 | + if (has_messages()) then | |
| 183 | + call apply_message(read_message()) | |
| 184 | + end if | |
| 185 | + end do | |
| 186 | + call gtk_widget_queue_draw(widget_ptr) | |
| 187 | + continue = 1_c_int | |
| 188 | + end function | |
| 189 | + | |
| 190 | +end module threaded_scanner | |
| 191 | +``` | |
| 192 | + | |
| 193 | +## Implementation Phases | |
| 194 | + | |
| 195 | +### Phase 1: Foundation (1-2 days) | |
| 196 | +- [ ] Add `scan_complete` flag to `file_node` type | |
| 197 | +- [ ] Add `estimated_size` vs `actual_size` fields | |
| 198 | +- [ ] Modify `scan_directory` to be interruptible (return after N files) | |
| 199 | +- [ ] Create `scan_state` module to track progress | |
| 200 | + | |
| 201 | +### Phase 2: Idle Callback Scanning (1 day) | |
| 202 | +- [ ] Implement breadth-first scan queue | |
| 203 | +- [ ] Add `g_idle_add` callback for progressive scanning | |
| 204 | +- [ ] Update sizes incrementally as scan progresses | |
| 205 | +- [ ] Trigger redraws after each directory | |
| 206 | + | |
| 207 | +### Phase 3: Dynamic Layout (2-3 days) | |
| 208 | +- [ ] Cache last layout in tree nodes | |
| 209 | +- [ ] Implement layout diffing (detect what changed) | |
| 210 | +- [ ] Partial layout recalculation | |
| 211 | +- [ ] Smooth transitions (animate box movements) | |
| 212 | + | |
| 213 | +### Phase 4: Performance Optimization (1-2 days) | |
| 214 | +- [ ] Throttle redraws (max 30 FPS) | |
| 215 | +- [ ] Batch size updates before layout | |
| 216 | +- [ ] Only re-layout visible nodes | |
| 217 | +- [ ] Add "scan paused" state for user interaction | |
| 218 | + | |
| 219 | +### Phase 5: Threading (Optional, 2-3 days) | |
| 220 | +- [ ] Implement background thread scanner | |
| 221 | +- [ ] Message queue for thread communication | |
| 222 | +- [ ] Lock-free or minimal locking strategy | |
| 223 | +- [ ] Test on multi-core systems | |
| 224 | + | |
| 225 | +## Visual Design Decisions | |
| 226 | + | |
| 227 | +### 1. **Incomplete Nodes Visual Indicator** | |
| 228 | +- **Option A**: Pulsing border while scanning | |
| 229 | +- **Option B**: Different color saturation (dimmer until complete) | |
| 230 | +- **Option C**: Animated gradient sweep | |
| 231 | +- **Recommendation**: Subtle pulse + slightly dimmer | |
| 232 | + | |
| 233 | +### 2. **Transition Animation** | |
| 234 | +- **Duration**: 100-200ms per size update | |
| 235 | +- **Easing**: Ease-out for natural feel | |
| 236 | +- **Method**: Linear interpolation in `render_node()` | |
| 237 | + | |
| 238 | +### 3. **Performance Targets** | |
| 239 | +- **Min framerate**: 30 FPS during scan | |
| 240 | +- **Max layout time**: 16ms per frame (60 FPS budget) | |
| 241 | +- **Redraw throttle**: Every 3-5 directories scanned | |
| 242 | + | |
| 243 | +## Edge Cases to Handle | |
| 244 | + | |
| 245 | +1. **Permission denied**: Mark node as complete with error state | |
| 246 | +2. **Symlink loops**: Detect and skip | |
| 247 | +3. **Very deep trees**: Limit recursion depth | |
| 248 | +4. **Very wide directories**: Limit children per node (group small files) | |
| 249 | +5. **Rapid navigation**: Cancel in-progress scans | |
| 250 | +6. **Window resize during scan**: Re-layout without restarting scan | |
| 251 | + | |
| 252 | +## Testing Strategy | |
| 253 | + | |
| 254 | +1. **Unit tests**: | |
| 255 | + - Scan queue push/pop | |
| 256 | + - Message queue thread safety | |
| 257 | + - Layout diffing algorithm | |
| 258 | + | |
| 259 | +2. **Integration tests**: | |
| 260 | + - Scan small directory (10 files) | |
| 261 | + - Scan deep directory (10 levels) | |
| 262 | + - Scan wide directory (1000 files) | |
| 263 | + - Navigate during scan | |
| 264 | + | |
| 265 | +3. **Performance tests**: | |
| 266 | + - Measure framerate on large directory | |
| 267 | + - Profile layout recalculation time | |
| 268 | + - Memory leak detection | |
| 269 | + | |
| 270 | +## Success Metrics | |
| 271 | + | |
| 272 | +- ✅ Boxes appear within 100ms of starting scan | |
| 273 | +- ✅ Smooth animation (30+ FPS) throughout scan | |
| 274 | +- ✅ No visual artifacts or flickering | |
| 275 | +- ✅ User can interact (click, navigate) during scan | |
| 276 | +- ✅ Memory usage stays reasonable (< 500MB for typical home directory) | |
| 277 | + | |
| 278 | +## Future Enhancements | |
| 279 | + | |
| 280 | +- **Scan prioritization**: Scan visible areas first | |
| 281 | +- **Incremental updates**: Update only changed subtrees | |
| 282 | +- **Predictive scanning**: Prefetch likely navigation targets | |
| 283 | +- **Network filesystem awareness**: Adjust timeouts for slow disks | |
| 284 | +- **Cancellation**: Allow user to stop scan mid-progress | |
| 285 | + | |
| 286 | +## References | |
| 287 | + | |
| 288 | +- SpaceSniffer behavior analysis: https://www.youtube.com/watch?v=... (TBD) | |
| 289 | +- GTK idle callbacks: https://docs.gtk.org/glib/func.idle_add.html | |
| 290 | +- Fortran OpenMP: https://www.openmp.org/wp-content/uploads/openmp-4.5.pdf | |
INSTALL.mdadded@@ -0,0 +1,97 @@ | ||
| 1 | +# Installing Sniffly | |
| 2 | + | |
| 3 | +Sniffly is a fast, visual disk space analyzer built with Fortran and GTK4. | |
| 4 | + | |
| 5 | +## Installation Methods | |
| 6 | + | |
| 7 | +### Option 1: Download DMG (Recommended) | |
| 8 | + | |
| 9 | +1. Download the latest release: [Sniffly-0.2.4-macOS.dmg](https://github.com/FortranGoingOnForty/sniffly/releases/latest) | |
| 10 | +2. Open the DMG | |
| 11 | +3. Drag `Sniffly.app` to your Applications folder | |
| 12 | +4. **First launch:** Right-click Sniffly → "Open" → Click "Open" again to bypass Gatekeeper | |
| 13 | + | |
| 14 | +### Option 2: Homebrew (For Brew Users) | |
| 15 | + | |
| 16 | +```bash | |
| 17 | +# Install Sniffly (once we publish the cask) | |
| 18 | +# No dependencies needed - everything is bundled! | |
| 19 | +brew install --cask sniffly | |
| 20 | +``` | |
| 21 | + | |
| 22 | +### Option 3: Build from Source | |
| 23 | + | |
| 24 | +```bash | |
| 25 | +# Install dependencies | |
| 26 | +brew install gtk4 gtk-4-fortran meson | |
| 27 | + | |
| 28 | +# Clone and build | |
| 29 | +git clone https://github.com/FortranGoingOnForty/sniffly.git | |
| 30 | +cd sniffly | |
| 31 | +meson setup build --buildtype=release | |
| 32 | +meson compile -C build | |
| 33 | + | |
| 34 | +# Run | |
| 35 | +./build/sniffly | |
| 36 | +``` | |
| 37 | + | |
| 38 | +## System Requirements | |
| 39 | + | |
| 40 | +- macOS 11.0 or later | |
| 41 | +- Apple Silicon or Intel Mac | |
| 42 | + | |
| 43 | +## Usage | |
| 44 | + | |
| 45 | +### Launch from Applications | |
| 46 | +Double-click Sniffly in your Applications folder. | |
| 47 | + | |
| 48 | +### Launch from Terminal | |
| 49 | +```bash | |
| 50 | +# Scan your home directory (default) | |
| 51 | +/Applications/Sniffly.app/Contents/MacOS/sniffly | |
| 52 | + | |
| 53 | +# Scan a specific directory | |
| 54 | +/Applications/Sniffly.app/Contents/MacOS/sniffly ~/Documents | |
| 55 | + | |
| 56 | +# Show help | |
| 57 | +/Applications/Sniffly.app/Contents/MacOS/sniffly --help | |
| 58 | + | |
| 59 | +# Show version | |
| 60 | +/Applications/Sniffly.app/Contents/MacOS/sniffly --version | |
| 61 | +``` | |
| 62 | + | |
| 63 | +## Troubleshooting | |
| 64 | + | |
| 65 | +### "Sniffly is damaged and can't be opened" | |
| 66 | + | |
| 67 | +This happens because the app isn't signed with an Apple Developer certificate. To bypass: | |
| 68 | + | |
| 69 | +**Method 1: Right-click to open** | |
| 70 | +1. Right-click (or Control-click) Sniffly.app | |
| 71 | +2. Click "Open" | |
| 72 | +3. Click "Open" again in the dialog | |
| 73 | + | |
| 74 | +**Method 2: Remove quarantine attribute** | |
| 75 | +```bash | |
| 76 | +xattr -cr /Applications/Sniffly.app | |
| 77 | +``` | |
| 78 | + | |
| 79 | +### Window doesn't appear | |
| 80 | + | |
| 81 | +If Sniffly starts but no window appears, try running from terminal to see debug output: | |
| 82 | +```bash | |
| 83 | +/Applications/Sniffly.app/Contents/MacOS/sniffly ~/Downloads | |
| 84 | +``` | |
| 85 | + | |
| 86 | +## Uninstalling | |
| 87 | + | |
| 88 | +### DMG Installation | |
| 89 | +```bash | |
| 90 | +rm -rf /Applications/Sniffly.app | |
| 91 | +rm ~/Library/Logs/Sniffly.log | |
| 92 | +``` | |
| 93 | + | |
| 94 | +### Homebrew Installation | |
| 95 | +```bash | |
| 96 | +brew uninstall --cask sniffly | |
| 97 | +``` | |
MACOS_PACKAGING_WALKTHROUGH.mdadded@@ -0,0 +1,357 @@ | ||
| 1 | +# Sniffly macOS Packaging Walkthrough | |
| 2 | + | |
| 3 | +This is a step-by-step guide to package Sniffly for macOS distribution. | |
| 4 | + | |
| 5 | +## Prerequisites | |
| 6 | + | |
| 7 | +Before starting, make sure you have: | |
| 8 | +- ✅ Sniffly built successfully (`./build/sniffly` exists) | |
| 9 | +- ✅ `dylibbundler` installed: `brew install dylibbundler` | |
| 10 | +- ✅ An application icon (optional, but recommended) | |
| 11 | +- 📝 Apple Developer account (only needed for code signing) | |
| 12 | + | |
| 13 | +## Version Management | |
| 14 | + | |
| 15 | +The version is controlled by a single `VERSION` file at the project root. To release a new version: | |
| 16 | + | |
| 17 | +```bash | |
| 18 | +echo "0.3.0" > VERSION | |
| 19 | +meson setup build --reconfigure | |
| 20 | +meson compile -C build | |
| 21 | +``` | |
| 22 | + | |
| 23 | +The version automatically appears in: | |
| 24 | +- The binary output (`./build/sniffly --version`) | |
| 25 | +- Info.plist in the app bundle | |
| 26 | +- The DMG filename | |
| 27 | + | |
| 28 | +## Quick Start: Automated Packaging | |
| 29 | + | |
| 30 | +```bash | |
| 31 | +# One command to create the .app and .dmg | |
| 32 | +./scripts/package-macos.sh | |
| 33 | +``` | |
| 34 | + | |
| 35 | +This creates: | |
| 36 | +- `Sniffly.app` - The application bundle | |
| 37 | +- `Sniffly-0.2.0-macOS.dmg` - Distributable installer | |
| 38 | + | |
| 39 | +### What The Script Does | |
| 40 | + | |
| 41 | +1. Reads version from `VERSION` file | |
| 42 | +2. Creates `.app` bundle structure | |
| 43 | +3. Copies the executable | |
| 44 | +4. Creates `Info.plist` with version info | |
| 45 | +5. Bundles GTK4 libraries using `dylibbundler` | |
| 46 | +6. Creates a distributable DMG | |
| 47 | + | |
| 48 | +## Step-by-Step Manual Process | |
| 49 | + | |
| 50 | +If you want to understand what's happening or customize the process: | |
| 51 | + | |
| 52 | +### Step 1: Build the Release Binary | |
| 53 | + | |
| 54 | +```bash | |
| 55 | +# Clean build for release | |
| 56 | +rm -rf build | |
| 57 | +meson setup build --buildtype=release | |
| 58 | +meson compile -C build | |
| 59 | + | |
| 60 | +# Verify it works | |
| 61 | +./build/sniffly --version | |
| 62 | +``` | |
| 63 | + | |
| 64 | +### Step 2: Create the App Bundle Structure | |
| 65 | + | |
| 66 | +```bash | |
| 67 | +mkdir -p Sniffly.app/Contents/MacOS | |
| 68 | +mkdir -p Sniffly.app/Contents/Resources | |
| 69 | +mkdir -p Sniffly.app/Contents/Frameworks | |
| 70 | +``` | |
| 71 | + | |
| 72 | +### Step 3: Copy the Executable | |
| 73 | + | |
| 74 | +```bash | |
| 75 | +cp build/sniffly Sniffly.app/Contents/MacOS/sniffly | |
| 76 | +chmod +x Sniffly.app/Contents/MacOS/sniffly | |
| 77 | +``` | |
| 78 | + | |
| 79 | +### Step 4: Create Info.plist | |
| 80 | + | |
| 81 | +```bash | |
| 82 | +cat > Sniffly.app/Contents/Info.plist << 'EOF' | |
| 83 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 84 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| 85 | +<plist version="1.0"> | |
| 86 | +<dict> | |
| 87 | + <key>CFBundleExecutable</key> | |
| 88 | + <string>sniffly</string> | |
| 89 | + <key>CFBundleIdentifier</key> | |
| 90 | + <string>org.fortrangoingonforty.sniffly</string> | |
| 91 | + <key>CFBundleName</key> | |
| 92 | + <string>Sniffly</string> | |
| 93 | + <key>CFBundleDisplayName</key> | |
| 94 | + <string>Sniffly</string> | |
| 95 | + <key>CFBundleVersion</key> | |
| 96 | + <string>0.2.0</string> | |
| 97 | + <key>CFBundleShortVersionString</key> | |
| 98 | + <string>0.2.0</string> | |
| 99 | + <key>CFBundlePackageType</key> | |
| 100 | + <string>APPL</string> | |
| 101 | + <key>CFBundleIconFile</key> | |
| 102 | + <string>sniffly</string> | |
| 103 | + <key>LSMinimumSystemVersion</key> | |
| 104 | + <string>11.0</string> | |
| 105 | + <key>LSApplicationCategoryType</key> | |
| 106 | + <string>public.app-category.utilities</string> | |
| 107 | +</dict> | |
| 108 | +</plist> | |
| 109 | +EOF | |
| 110 | +``` | |
| 111 | + | |
| 112 | +### Step 5: Add an Icon (Optional but Recommended) | |
| 113 | + | |
| 114 | +You need a `.icns` file. If you have a 1024x1024 PNG: | |
| 115 | + | |
| 116 | +```bash | |
| 117 | +# Create iconset directory | |
| 118 | +mkdir sniffly.iconset | |
| 119 | + | |
| 120 | +# Generate all sizes | |
| 121 | +sips -z 16 16 icon-1024.png --out sniffly.iconset/icon_16x16.png | |
| 122 | +sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_16x16@2x.png | |
| 123 | +sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_32x32.png | |
| 124 | +sips -z 64 64 icon-1024.png --out sniffly.iconset/icon_32x32@2x.png | |
| 125 | +sips -z 128 128 icon-1024.png --out sniffly.iconset/icon_128x128.png | |
| 126 | +sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_128x128@2x.png | |
| 127 | +sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_256x256.png | |
| 128 | +sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_256x256@2x.png | |
| 129 | +sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_512x512.png | |
| 130 | +sips -z 1024 1024 icon-1024.png --out sniffly.iconset/icon_512x512@2x.png | |
| 131 | + | |
| 132 | +# Convert to .icns | |
| 133 | +iconutil -c icns sniffly.iconset -o Sniffly.app/Contents/Resources/sniffly.icns | |
| 134 | + | |
| 135 | +# Cleanup | |
| 136 | +rm -rf sniffly.iconset | |
| 137 | +``` | |
| 138 | + | |
| 139 | +### Step 6: Bundle GTK4 Libraries | |
| 140 | + | |
| 141 | +This is the crucial step that makes your app "just work" on any Mac: | |
| 142 | + | |
| 143 | +```bash | |
| 144 | +dylibbundler -od -b \ | |
| 145 | + -x Sniffly.app/Contents/MacOS/sniffly \ | |
| 146 | + -d Sniffly.app/Contents/Frameworks/ \ | |
| 147 | + -p @executable_path/../Frameworks/ | |
| 148 | +``` | |
| 149 | + | |
| 150 | +What this does: | |
| 151 | +- Finds all dylib dependencies (GTK4, Cairo, GLib, etc.) | |
| 152 | +- Copies them into `Frameworks/` | |
| 153 | +- Rewrites the executable to look in `@executable_path/../Frameworks/` | |
| 154 | +- Makes the app fully self-contained | |
| 155 | + | |
| 156 | +**Expected size**: ~100-150MB (GTK4 is big!) | |
| 157 | + | |
| 158 | +### Step 7: Test the App Bundle | |
| 159 | + | |
| 160 | +```bash | |
| 161 | +open Sniffly.app | |
| 162 | +``` | |
| 163 | + | |
| 164 | +If it works, you're ready to create the DMG! | |
| 165 | + | |
| 166 | +### Step 8: Create the DMG | |
| 167 | + | |
| 168 | +```bash | |
| 169 | +# Create temporary folder | |
| 170 | +mkdir dmg_temp | |
| 171 | +cp -r Sniffly.app dmg_temp/ | |
| 172 | + | |
| 173 | +# Add Applications folder shortcut | |
| 174 | +ln -s /Applications dmg_temp/Applications | |
| 175 | + | |
| 176 | +# Create DMG | |
| 177 | +hdiutil create -volname "Sniffly" \ | |
| 178 | + -srcfolder dmg_temp \ | |
| 179 | + -ov -format UDZO \ | |
| 180 | + Sniffly-0.2.0-macOS.dmg | |
| 181 | + | |
| 182 | +# Cleanup | |
| 183 | +rm -rf dmg_temp | |
| 184 | +``` | |
| 185 | + | |
| 186 | +## Code Signing (Optional but Recommended) | |
| 187 | + | |
| 188 | +If you have an Apple Developer account: | |
| 189 | + | |
| 190 | +```bash | |
| 191 | +# Sign the app | |
| 192 | +codesign --deep --force --verify --verbose \ | |
| 193 | + --sign "Developer ID Application: Your Name (TEAM_ID)" \ | |
| 194 | + Sniffly.app | |
| 195 | + | |
| 196 | +# Verify signature | |
| 197 | +codesign -dv Sniffly.app | |
| 198 | + | |
| 199 | +# Create DMG from signed app | |
| 200 | +# (repeat Step 8 after signing) | |
| 201 | + | |
| 202 | +# Notarize (required for distribution outside App Store) | |
| 203 | +xcrun notarytool submit Sniffly-0.2.0-macOS.dmg \ | |
| 204 | + --apple-id "your@email.com" \ | |
| 205 | + --password "app-specific-password" \ | |
| 206 | + --team-id "YOUR_TEAM_ID" \ | |
| 207 | + --wait | |
| 208 | + | |
| 209 | +# Staple notarization ticket | |
| 210 | +xcrun stapler staple Sniffly-0.2.0-macOS.dmg | |
| 211 | +``` | |
| 212 | + | |
| 213 | +**Why sign and notarize?** | |
| 214 | +- Without: Users see "unidentified developer" warning | |
| 215 | +- With: Clean installation experience, no warnings | |
| 216 | + | |
| 217 | +**Cost**: $99/year for Apple Developer account | |
| 218 | + | |
| 219 | +## Distribution | |
| 220 | + | |
| 221 | +Once you have `Sniffly-0.2.0-macOS.dmg`: | |
| 222 | + | |
| 223 | +1. **Test on a clean Mac** (or VM) without development tools installed | |
| 224 | +2. **Upload to your server**: | |
| 225 | + ```bash | |
| 226 | + scp Sniffly-0.2.0-macOS.dmg user@yourserver.com:/var/www/downloads/ | |
| 227 | + ``` | |
| 228 | +3. **Create a download page** with: | |
| 229 | + - Link to the DMG | |
| 230 | + - System requirements (macOS 11.0+) | |
| 231 | + - Installation instructions | |
| 232 | + - Screenshot/demo | |
| 233 | + | |
| 234 | +### Example Download Page | |
| 235 | + | |
| 236 | +```html | |
| 237 | +<!DOCTYPE html> | |
| 238 | +<html> | |
| 239 | +<head> | |
| 240 | + <title>Download Sniffly</title> | |
| 241 | +</head> | |
| 242 | +<body> | |
| 243 | + <h1>Download Sniffly v0.2.0</h1> | |
| 244 | + <p>A fast, visual disk space analyzer for macOS</p> | |
| 245 | + | |
| 246 | + <a href="Sniffly-0.2.0-macOS.dmg" class="download-button"> | |
| 247 | + Download for macOS (150 MB) | |
| 248 | + </a> | |
| 249 | + | |
| 250 | + <h2>Requirements</h2> | |
| 251 | + <ul> | |
| 252 | + <li>macOS 11.0 (Big Sur) or later</li> | |
| 253 | + <li>Apple Silicon or Intel processor</li> | |
| 254 | + </ul> | |
| 255 | + | |
| 256 | + <h2>Installation</h2> | |
| 257 | + <ol> | |
| 258 | + <li>Download Sniffly-0.2.0-macOS.dmg</li> | |
| 259 | + <li>Open the DMG file</li> | |
| 260 | + <li>Drag Sniffly to Applications folder</li> | |
| 261 | + <li>Launch from Applications</li> | |
| 262 | + </ol> | |
| 263 | +</body> | |
| 264 | +</html> | |
| 265 | +``` | |
| 266 | + | |
| 267 | +## Release Checklist | |
| 268 | + | |
| 269 | +Before releasing a new version: | |
| 270 | + | |
| 271 | +- [ ] Update `VERSION` file | |
| 272 | +- [ ] Test on clean macOS installation | |
| 273 | +- [ ] Verify `--version` shows correct version | |
| 274 | +- [ ] Test basic functionality (scan, navigation, etc.) | |
| 275 | +- [ ] Build release binary (`--buildtype=release`) | |
| 276 | +- [ ] Bundle GTK4 libraries with dylibbundler | |
| 277 | +- [ ] Test app bundle works standalone | |
| 278 | +- [ ] Create DMG | |
| 279 | +- [ ] (Optional) Code sign and notarize | |
| 280 | +- [ ] Upload to distribution server | |
| 281 | +- [ ] Update download page | |
| 282 | +- [ ] Announce release (social media, website, etc.) | |
| 283 | + | |
| 284 | +## Troubleshooting | |
| 285 | + | |
| 286 | +### "Sniffly is damaged and can't be opened" | |
| 287 | + | |
| 288 | +This happens when macOS quarantines unsigned apps. Users can bypass with: | |
| 289 | +```bash | |
| 290 | +xattr -cr /Applications/Sniffly.app | |
| 291 | +``` | |
| 292 | + | |
| 293 | +Or you need to code sign and notarize the app. | |
| 294 | + | |
| 295 | +### App crashes immediately | |
| 296 | + | |
| 297 | +- Check if GTK4 libraries are bundled: `ls Sniffly.app/Contents/Frameworks/` | |
| 298 | +- Should see many `.dylib` files | |
| 299 | +- Verify with: `otool -L Sniffly.app/Contents/MacOS/sniffly` | |
| 300 | + - Should show `@executable_path/../Frameworks/` paths | |
| 301 | + | |
| 302 | +### DMG too large (>200MB) | |
| 303 | + | |
| 304 | +Normal! GTK4 is big. Options: | |
| 305 | +- Accept it (users download once) | |
| 306 | +- Require system GTK4 (smaller DMG, but users must `brew install gtk4`) | |
| 307 | + | |
| 308 | +### Can't sign the app | |
| 309 | + | |
| 310 | +You need an Apple Developer account ($99/year). For free distribution: | |
| 311 | +- Skip signing (users will see warning) | |
| 312 | +- Document how to bypass Gatekeeper | |
| 313 | + | |
| 314 | +## Version Number Advice | |
| 315 | + | |
| 316 | +You asked about version numbering. Here's my take: | |
| 317 | + | |
| 318 | +**Current State of Sniffly (0.2.0 suggestion)**: | |
| 319 | +- ✅ Core functionality works (scanning, treemap, navigation) | |
| 320 | +- ✅ Progressive scanning is solid | |
| 321 | +- ✅ 3D effects, navigation locking | |
| 322 | +- ⚠️ Some rough edges (had bugs we fixed) | |
| 323 | +- ⚠️ No installers yet (this guide creates them) | |
| 324 | +- ❌ Not feature-complete vs original SpaceSniffer | |
| 325 | + | |
| 326 | +**Version recommendations**: | |
| 327 | +- **0.1.0** - Tech preview, barely works | |
| 328 | +- **0.2.0** - Alpha release (current state) ← **I recommend this** | |
| 329 | +- **0.5.0** - Beta release (feature complete, but bugs) | |
| 330 | +- **0.9.0** - Release candidate | |
| 331 | +- **1.0.0** - Stable, production-ready | |
| 332 | + | |
| 333 | +**Why 0.2.0?** | |
| 334 | +- Shows progress from initial experiments | |
| 335 | +- Sets expectations (alpha quality) | |
| 336 | +- Room to grow before 1.0 | |
| 337 | +- Conservative (you prefer this) | |
| 338 | + | |
| 339 | +**Semantic versioning for future**: | |
| 340 | +- `0.x.y` - Pre-1.0 development | |
| 341 | +- `1.0.0` - First stable release | |
| 342 | +- `1.1.0` - Add features (backwards compatible) | |
| 343 | +- `1.0.1` - Bug fixes | |
| 344 | +- `2.0.0` - Breaking changes | |
| 345 | + | |
| 346 | +## Next Steps | |
| 347 | + | |
| 348 | +After successful macOS packaging, consider: | |
| 349 | +- Linux AppImage (simpler than .deb/.rpm) | |
| 350 | +- Homebrew cask formula (`brew install --cask sniffly`) | |
| 351 | +- Automatic updates (Sparkle framework) | |
| 352 | +- Analytics (crash reporting, usage stats) | |
| 353 | +- Website with documentation | |
| 354 | + | |
| 355 | +--- | |
| 356 | + | |
| 357 | +**Questions?** The packaging script at `scripts/package-macos.sh` automates all of this! | |
PACKAGING.mdadded@@ -0,0 +1,440 @@ | ||
| 1 | +# Sniffly Packaging Guide | |
| 2 | + | |
| 3 | +This document explains how to package Sniffly for distribution on macOS and Linux. | |
| 4 | + | |
| 5 | +## Current State | |
| 6 | + | |
| 7 | +Sniffly is built using: | |
| 8 | +- **Meson** build system | |
| 9 | +- **GTK4** for the GUI | |
| 10 | +- **gtk-fortran** bindings | |
| 11 | +- **gfortran** compiler (GCC Fortran) | |
| 12 | + | |
| 13 | +The executable is currently located at `build/sniffly` after running `meson compile -C build`. | |
| 14 | + | |
| 15 | +--- | |
| 16 | + | |
| 17 | +## macOS Packaging (.dmg) | |
| 18 | + | |
| 19 | +### Overview | |
| 20 | +On macOS, applications are distributed as: | |
| 21 | +1. **.app bundles** - Directory structure that looks like a single file | |
| 22 | +2. **.dmg files** - Disk images containing the .app bundle | |
| 23 | + | |
| 24 | +### Steps to Create a macOS .app Bundle | |
| 25 | + | |
| 26 | +#### 1. Create the Bundle Structure | |
| 27 | + | |
| 28 | +```bash | |
| 29 | +# Create the app bundle directories | |
| 30 | +mkdir -p Sniffly.app/Contents/MacOS | |
| 31 | +mkdir -p Sniffly.app/Contents/Resources | |
| 32 | +mkdir -p Sniffly.app/Contents/Frameworks | |
| 33 | +``` | |
| 34 | + | |
| 35 | +#### 2. Copy the Executable | |
| 36 | + | |
| 37 | +```bash | |
| 38 | +# Copy the compiled binary | |
| 39 | +cp build/sniffly Sniffly.app/Contents/MacOS/sniffly | |
| 40 | +``` | |
| 41 | + | |
| 42 | +#### 3. Create Info.plist | |
| 43 | + | |
| 44 | +Create `Sniffly.app/Contents/Info.plist`: | |
| 45 | + | |
| 46 | +```xml | |
| 47 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 48 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| 49 | +<plist version="1.0"> | |
| 50 | +<dict> | |
| 51 | + <key>CFBundleExecutable</key> | |
| 52 | + <string>sniffly</string> | |
| 53 | + <key>CFBundleIdentifier</key> | |
| 54 | + <string>org.fortrangoingonforty.sniffly</string> | |
| 55 | + <key>CFBundleName</key> | |
| 56 | + <string>Sniffly</string> | |
| 57 | + <key>CFBundleDisplayName</key> | |
| 58 | + <string>Sniffly</string> | |
| 59 | + <key>CFBundleVersion</key> | |
| 60 | + <string>0.1.0</string> | |
| 61 | + <key>CFBundleShortVersionString</key> | |
| 62 | + <string>0.1.0</string> | |
| 63 | + <key>CFBundlePackageType</key> | |
| 64 | + <string>APPL</string> | |
| 65 | + <key>CFBundleSignature</key> | |
| 66 | + <string>SNIF</string> | |
| 67 | + <key>CFBundleIconFile</key> | |
| 68 | + <string>sniffly</string> | |
| 69 | + <key>LSMinimumSystemVersion</key> | |
| 70 | + <string>11.0</string> | |
| 71 | + <key>NSHighResolutionCapable</key> | |
| 72 | + <true/> | |
| 73 | + <key>NSRequiresAquaSystemAppearance</key> | |
| 74 | + <false/> | |
| 75 | +</dict> | |
| 76 | +</plist> | |
| 77 | +``` | |
| 78 | + | |
| 79 | +#### 4. Create an Application Icon | |
| 80 | + | |
| 81 | +You need a `.icns` file. Create one from a PNG using: | |
| 82 | + | |
| 83 | +```bash | |
| 84 | +# Create iconset directory | |
| 85 | +mkdir sniffly.iconset | |
| 86 | + | |
| 87 | +# Create different sizes (you'll need a source image) | |
| 88 | +# 16x16, 32x32, 128x128, 256x256, 512x512, 1024x1024 | |
| 89 | +sips -z 16 16 icon-1024.png --out sniffly.iconset/icon_16x16.png | |
| 90 | +sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_16x16@2x.png | |
| 91 | +sips -z 32 32 icon-1024.png --out sniffly.iconset/icon_32x32.png | |
| 92 | +sips -z 64 64 icon-1024.png --out sniffly.iconset/icon_32x32@2x.png | |
| 93 | +sips -z 128 128 icon-1024.png --out sniffly.iconset/icon_128x128.png | |
| 94 | +sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_128x128@2x.png | |
| 95 | +sips -z 256 256 icon-1024.png --out sniffly.iconset/icon_256x256.png | |
| 96 | +sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_256x256@2x.png | |
| 97 | +sips -z 512 512 icon-1024.png --out sniffly.iconset/icon_512x512.png | |
| 98 | +sips -z 1024 1024 icon-1024.png --out sniffly.iconset/icon_512x512@2x.png | |
| 99 | + | |
| 100 | +# Convert to .icns | |
| 101 | +iconutil -c icns sniffly.iconset -o Sniffly.app/Contents/Resources/sniffly.icns | |
| 102 | +``` | |
| 103 | + | |
| 104 | +#### 5. Bundle GTK4 Libraries | |
| 105 | + | |
| 106 | +This is the tricky part! The app needs GTK4 libraries. You have two options: | |
| 107 | + | |
| 108 | +**Option A: Use Homebrew's GTK4 (requires user to have GTK4 installed)** | |
| 109 | +```bash | |
| 110 | +# User must run: brew install gtk4 | |
| 111 | +# Then the app will find libraries via dylib search paths | |
| 112 | +``` | |
| 113 | + | |
| 114 | +**Option B: Bundle libraries (recommended for distribution)** | |
| 115 | + | |
| 116 | +```bash | |
| 117 | +# Copy GTK4 libraries into the bundle | |
| 118 | +cp -r /opt/homebrew/lib/libgtk-4*.dylib Sniffly.app/Contents/Frameworks/ | |
| 119 | +cp -r /opt/homebrew/lib/libglib-2.0*.dylib Sniffly.app/Contents/Frameworks/ | |
| 120 | +cp -r /opt/homebrew/lib/libgobject-2.0*.dylib Sniffly.app/Contents/Frameworks/ | |
| 121 | +# ... and all other GTK4 dependencies | |
| 122 | + | |
| 123 | +# Use install_name_tool to fix library paths | |
| 124 | +install_name_tool -change /opt/homebrew/lib/libgtk-4.dylib \ | |
| 125 | + @executable_path/../Frameworks/libgtk-4.dylib \ | |
| 126 | + Sniffly.app/Contents/MacOS/sniffly | |
| 127 | +``` | |
| 128 | + | |
| 129 | +**Better Option: Use `dylibbundler`** | |
| 130 | +```bash | |
| 131 | +# Install dylibbundler | |
| 132 | +brew install dylibbundler | |
| 133 | + | |
| 134 | +# Automatically bundle all dependencies | |
| 135 | +dylibbundler -od -b \ | |
| 136 | + -x Sniffly.app/Contents/MacOS/sniffly \ | |
| 137 | + -d Sniffly.app/Contents/Frameworks/ \ | |
| 138 | + -p @executable_path/../Frameworks/ | |
| 139 | +``` | |
| 140 | + | |
| 141 | +#### 6. Create the .dmg | |
| 142 | + | |
| 143 | +```bash | |
| 144 | +# Create a temporary directory for DMG contents | |
| 145 | +mkdir dmg_temp | |
| 146 | +cp -r Sniffly.app dmg_temp/ | |
| 147 | + | |
| 148 | +# Optional: Create a symbolic link to /Applications for easy drag-install | |
| 149 | +ln -s /Applications dmg_temp/Applications | |
| 150 | + | |
| 151 | +# Create the DMG | |
| 152 | +hdiutil create -volname "Sniffly" \ | |
| 153 | + -srcfolder dmg_temp \ | |
| 154 | + -ov -format UDZO \ | |
| 155 | + Sniffly-0.1.0-macOS.dmg | |
| 156 | + | |
| 157 | +# Clean up | |
| 158 | +rm -rf dmg_temp | |
| 159 | +``` | |
| 160 | + | |
| 161 | +#### 7. Code Signing (Optional but Recommended) | |
| 162 | + | |
| 163 | +```bash | |
| 164 | +# Sign the app (requires Apple Developer account) | |
| 165 | +codesign --deep --force --verify --verbose \ | |
| 166 | + --sign "Developer ID Application: Your Name" \ | |
| 167 | + Sniffly.app | |
| 168 | + | |
| 169 | +# Notarize with Apple (required for distribution outside App Store) | |
| 170 | +xcrun notarytool submit Sniffly-0.1.0-macOS.dmg \ | |
| 171 | + --apple-id "your@email.com" \ | |
| 172 | + --password "app-specific-password" \ | |
| 173 | + --team-id "YOUR_TEAM_ID" \ | |
| 174 | + --wait | |
| 175 | + | |
| 176 | +# Staple the notarization ticket | |
| 177 | +xcrun stapler staple Sniffly-0.1.0-macOS.dmg | |
| 178 | +``` | |
| 179 | + | |
| 180 | +--- | |
| 181 | + | |
| 182 | +## Linux Packaging | |
| 183 | + | |
| 184 | +### Option 1: AppImage (Recommended - Works on All Distros) | |
| 185 | + | |
| 186 | +AppImage is a universal package format that works across all Linux distributions. | |
| 187 | + | |
| 188 | +#### Create AppImage Structure | |
| 189 | + | |
| 190 | +```bash | |
| 191 | +# Create AppDir structure | |
| 192 | +mkdir -p Sniffly.AppDir/usr/bin | |
| 193 | +mkdir -p Sniffly.AppDir/usr/lib | |
| 194 | +mkdir -p Sniffly.AppDir/usr/share/applications | |
| 195 | +mkdir -p Sniffly.AppDir/usr/share/icons/hicolor/256x256/apps | |
| 196 | + | |
| 197 | +# Copy executable | |
| 198 | +cp build/sniffly Sniffly.AppDir/usr/bin/ | |
| 199 | + | |
| 200 | +# Copy icon | |
| 201 | +cp assets/sniffly.png Sniffly.AppDir/usr/share/icons/hicolor/256x256/apps/sniffly.png | |
| 202 | +cp assets/sniffly.png Sniffly.AppDir/sniffly.png | |
| 203 | + | |
| 204 | +# Create .desktop file | |
| 205 | +cat > Sniffly.AppDir/sniffly.desktop << 'EOF' | |
| 206 | +[Desktop Entry] | |
| 207 | +Type=Application | |
| 208 | +Name=Sniffly | |
| 209 | +Comment=Disk Space Analyzer | |
| 210 | +Exec=sniffly | |
| 211 | +Icon=sniffly | |
| 212 | +Categories=Utility;System; | |
| 213 | +Terminal=false | |
| 214 | +EOF | |
| 215 | + | |
| 216 | +# Copy to required location | |
| 217 | +cp Sniffly.AppDir/sniffly.desktop Sniffly.AppDir/usr/share/applications/ | |
| 218 | + | |
| 219 | +# Create AppRun script | |
| 220 | +cat > Sniffly.AppDir/AppRun << 'EOF' | |
| 221 | +#!/bin/bash | |
| 222 | +SELF=$(readlink -f "$0") | |
| 223 | +HERE=${SELF%/*} | |
| 224 | +export PATH="${HERE}/usr/bin:${PATH}" | |
| 225 | +export LD_LIBRARY_PATH="${HERE}/usr/lib:${LD_LIBRARY_PATH}" | |
| 226 | +export XDG_DATA_DIRS="${HERE}/usr/share:${XDG_DATA_DIRS}" | |
| 227 | +exec "${HERE}/usr/bin/sniffly" "$@" | |
| 228 | +EOF | |
| 229 | + | |
| 230 | +chmod +x Sniffly.AppDir/AppRun | |
| 231 | + | |
| 232 | +# Download appimagetool | |
| 233 | +wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage | |
| 234 | +chmod +x appimagetool-x86_64.AppImage | |
| 235 | + | |
| 236 | +# Build AppImage | |
| 237 | +./appimagetool-x86_64.AppImage Sniffly.AppDir Sniffly-0.1.0-x86_64.AppImage | |
| 238 | +``` | |
| 239 | + | |
| 240 | +**Note**: AppImage requires GTK4 to be installed on the system. For a fully self-contained AppImage, you'd need to bundle GTK4 libraries. | |
| 241 | + | |
| 242 | +### Option 2: Debian Package (.deb) | |
| 243 | + | |
| 244 | +For Debian/Ubuntu users: | |
| 245 | + | |
| 246 | +```bash | |
| 247 | +# Create package structure | |
| 248 | +mkdir -p sniffly-0.1.0/DEBIAN | |
| 249 | +mkdir -p sniffly-0.1.0/usr/bin | |
| 250 | +mkdir -p sniffly-0.1.0/usr/share/applications | |
| 251 | +mkdir -p sniffly-0.1.0/usr/share/icons/hicolor/256x256/apps | |
| 252 | + | |
| 253 | +# Copy files | |
| 254 | +cp build/sniffly sniffly-0.1.0/usr/bin/ | |
| 255 | +cp assets/sniffly.png sniffly-0.1.0/usr/share/icons/hicolor/256x256/apps/ | |
| 256 | +cp assets/sniffly.desktop sniffly-0.1.0/usr/share/applications/ | |
| 257 | + | |
| 258 | +# Create control file | |
| 259 | +cat > sniffly-0.1.0/DEBIAN/control << 'EOF' | |
| 260 | +Package: sniffly | |
| 261 | +Version: 0.1.0 | |
| 262 | +Section: utils | |
| 263 | +Priority: optional | |
| 264 | +Architecture: amd64 | |
| 265 | +Depends: libgtk-4-1, libglib2.0-0 | |
| 266 | +Maintainer: FortranGoingOnForty <your@email.com> | |
| 267 | +Description: Disk Space Analyzer | |
| 268 | + A fast, beautiful disk space analyzer built with Fortran and GTK4. | |
| 269 | + Visualizes disk usage with interactive treemaps. | |
| 270 | +EOF | |
| 271 | + | |
| 272 | +# Build the package | |
| 273 | +dpkg-deb --build sniffly-0.1.0 | |
| 274 | +``` | |
| 275 | + | |
| 276 | +### Option 3: RPM Package (Fedora/RedHat) | |
| 277 | + | |
| 278 | +```bash | |
| 279 | +# Create RPM build directories | |
| 280 | +mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} | |
| 281 | + | |
| 282 | +# Create spec file | |
| 283 | +cat > ~/rpmbuild/SPECS/sniffly.spec << 'EOF' | |
| 284 | +Name: sniffly | |
| 285 | +Version: 0.1.0 | |
| 286 | +Release: 1%{?dist} | |
| 287 | +Summary: Disk Space Analyzer | |
| 288 | +License: MIT | |
| 289 | +URL: https://github.com/FortranGoingOnForty/sniffly | |
| 290 | +Source0: %{name}-%{version}.tar.gz | |
| 291 | + | |
| 292 | +BuildRequires: meson, gcc-gfortran, gtk4-devel | |
| 293 | +Requires: gtk4 | |
| 294 | + | |
| 295 | +%description | |
| 296 | +A fast, beautiful disk space analyzer built with Fortran and GTK4. | |
| 297 | + | |
| 298 | +%prep | |
| 299 | +%setup -q | |
| 300 | + | |
| 301 | +%build | |
| 302 | +meson setup build | |
| 303 | +meson compile -C build | |
| 304 | + | |
| 305 | +%install | |
| 306 | +install -Dm755 build/sniffly %{buildroot}%{_bindir}/sniffly | |
| 307 | +install -Dm644 assets/sniffly.desktop %{buildroot}%{_datadir}/applications/sniffly.desktop | |
| 308 | +install -Dm644 assets/sniffly.png %{buildroot}%{_datadir}/icons/hicolor/256x256/apps/sniffly.png | |
| 309 | + | |
| 310 | +%files | |
| 311 | +%{_bindir}/sniffly | |
| 312 | +%{_datadir}/applications/sniffly.desktop | |
| 313 | +%{_datadir}/icons/hicolor/256x256/apps/sniffly.png | |
| 314 | + | |
| 315 | +%changelog | |
| 316 | +* Mon Jan 01 2024 Your Name <your@email.com> - 0.1.0-1 | |
| 317 | +- Initial release | |
| 318 | +EOF | |
| 319 | + | |
| 320 | +# Create source tarball | |
| 321 | +tar czf ~/rpmbuild/SOURCES/sniffly-0.1.0.tar.gz ../sniffly/ | |
| 322 | + | |
| 323 | +# Build RPM | |
| 324 | +rpmbuild -ba ~/rpmbuild/SPECS/sniffly.spec | |
| 325 | +``` | |
| 326 | + | |
| 327 | +--- | |
| 328 | + | |
| 329 | +## Automated Packaging with CI/CD | |
| 330 | + | |
| 331 | +### GitHub Actions Example | |
| 332 | + | |
| 333 | +Create `.github/workflows/release.yml`: | |
| 334 | + | |
| 335 | +```yaml | |
| 336 | +name: Release Build | |
| 337 | + | |
| 338 | +on: | |
| 339 | + push: | |
| 340 | + tags: | |
| 341 | + - 'v*' | |
| 342 | + | |
| 343 | +jobs: | |
| 344 | + build-macos: | |
| 345 | + runs-on: macos-latest | |
| 346 | + steps: | |
| 347 | + - uses: actions/checkout@v3 | |
| 348 | + - name: Install dependencies | |
| 349 | + run: | | |
| 350 | + brew install meson gtk4 gcc | |
| 351 | + - name: Build | |
| 352 | + run: | | |
| 353 | + meson setup build | |
| 354 | + meson compile -C build | |
| 355 | + - name: Create DMG | |
| 356 | + run: | | |
| 357 | + # Add DMG creation steps here | |
| 358 | + - name: Upload Release Asset | |
| 359 | + uses: actions/upload-artifact@v3 | |
| 360 | + with: | |
| 361 | + name: Sniffly-macOS.dmg | |
| 362 | + path: Sniffly-*.dmg | |
| 363 | + | |
| 364 | + build-linux: | |
| 365 | + runs-on: ubuntu-latest | |
| 366 | + steps: | |
| 367 | + - uses: actions/checkout@v3 | |
| 368 | + - name: Install dependencies | |
| 369 | + run: | | |
| 370 | + sudo apt-get update | |
| 371 | + sudo apt-get install -y meson gfortran libgtk-4-dev | |
| 372 | + - name: Build | |
| 373 | + run: | | |
| 374 | + meson setup build | |
| 375 | + meson compile -C build | |
| 376 | + - name: Create AppImage | |
| 377 | + run: | | |
| 378 | + # Add AppImage creation steps here | |
| 379 | + - name: Upload Release Asset | |
| 380 | + uses: actions/upload-artifact@v3 | |
| 381 | + with: | |
| 382 | + name: Sniffly-Linux-x86_64.AppImage | |
| 383 | + path: Sniffly-*.AppImage | |
| 384 | +``` | |
| 385 | + | |
| 386 | +--- | |
| 387 | + | |
| 388 | +## Quick Start Summary | |
| 389 | + | |
| 390 | +### macOS | |
| 391 | +1. Build the app: `meson compile -C build` | |
| 392 | +2. Create .app bundle structure | |
| 393 | +3. Use `dylibbundler` to bundle GTK4 libraries | |
| 394 | +4. Create .dmg with `hdiutil` | |
| 395 | +5. (Optional) Code sign and notarize | |
| 396 | + | |
| 397 | +**Result**: `Sniffly-0.1.0-macOS.dmg` | |
| 398 | + | |
| 399 | +### Linux | |
| 400 | +1. Build the app: `meson compile -C build` | |
| 401 | +2. Create AppDir structure | |
| 402 | +3. Use `appimagetool` to create AppImage | |
| 403 | +4. Or create .deb/.rpm for specific distros | |
| 404 | + | |
| 405 | +**Result**: `Sniffly-0.1.0-x86_64.AppImage` or `.deb`/`.rpm` | |
| 406 | + | |
| 407 | +--- | |
| 408 | + | |
| 409 | +## Distribution Checklist | |
| 410 | + | |
| 411 | +- [ ] Create application icon (1024x1024 PNG) | |
| 412 | +- [ ] Create .desktop file for Linux | |
| 413 | +- [ ] Test on clean system without development tools | |
| 414 | +- [ ] Document system requirements (GTK4, etc.) | |
| 415 | +- [ ] Add installation instructions to README | |
| 416 | +- [ ] Create GitHub releases page | |
| 417 | +- [ ] Consider Homebrew formula for macOS | |
| 418 | +- [ ] Consider Flatpak for Linux (alternative to AppImage) | |
| 419 | + | |
| 420 | +--- | |
| 421 | + | |
| 422 | +## Notes on GTK4 Dependencies | |
| 423 | + | |
| 424 | +Sniffly requires GTK4 at runtime. You have three options: | |
| 425 | + | |
| 426 | +1. **User installation**: Require users to install GTK4 (`brew install gtk4` on macOS, `apt install libgtk-4-1` on Debian) | |
| 427 | +2. **Bundle libraries**: Include GTK4 in your app bundle (increases size ~100MB but works anywhere) | |
| 428 | +3. **Hybrid**: Bundle on macOS, rely on system packages on Linux | |
| 429 | + | |
| 430 | +For the best user experience, option 2 (bundling) is recommended for macOS, while option 1 (system packages) works well for Linux where GTK4 is commonly available. | |
| 431 | + | |
| 432 | +--- | |
| 433 | + | |
| 434 | +## Future Improvements | |
| 435 | + | |
| 436 | +- **Flatpak**: Cross-distro sandboxed app (like macOS .app) | |
| 437 | +- **Snap**: Alternative Linux universal package | |
| 438 | +- **Homebrew Cask**: Easy installation on macOS (`brew install --cask sniffly`) | |
| 439 | +- **AUR Package**: For Arch Linux users | |
| 440 | +- **Windows**: Cross-compile with MinGW-w64 (if desired) | |
PROJECT_SUMMARY.mdadded@@ -0,0 +1,481 @@ | ||
| 1 | +# Sniffly Project Summary | |
| 2 | + | |
| 3 | +**Status:** Planning Complete ✅ | |
| 4 | +**Date:** 2025-11-04 | |
| 5 | +**Next Step:** Begin Phase 1 Development | |
| 6 | + | |
| 7 | +--- | |
| 8 | + | |
| 9 | +## What We're Building | |
| 10 | + | |
| 11 | +**Sniffly** is a pure Fortran GUI application that visualizes disk space usage with interactive treemap diagrams. It's a near 1:1 clone of SpaceSniffer (a popular Windows tool) but for Unix systems (macOS and Linux). | |
| 12 | + | |
| 13 | +### The Big Idea | |
| 14 | + | |
| 15 | +Prove that Fortran isn't just for scientific computing—it can build beautiful, modern GUI applications that rival commercial software written in C++/C#. | |
| 16 | + | |
| 17 | +--- | |
| 18 | + | |
| 19 | +## Technology Stack (Final Decision) | |
| 20 | + | |
| 21 | +| Component | Technology | Why | | |
| 22 | +|-----------|-----------|-----| | |
| 23 | +| **Language** | Fortran 2008+ | Pure Fortran philosophy, performance | | |
| 24 | +| **GUI Framework** | GTK4 | Modern, cross-platform, has Fortran bindings | | |
| 25 | +| **Bindings** | gtk-fortran 3.24.41+ | Only mature Fortran GUI library | | |
| 26 | +| **Graphics** | Cairo 1.18+ | Hardware-accelerated 2D drawing | | |
| 27 | +| **Text** | Pango | Font rendering, ellipsization | | |
| 28 | +| **Build System** | Meson (primary) | GTK standard, handles dependencies | | |
| 29 | +| **Platforms** | macOS, Linux | Priority targets (no Windows) | | |
| 30 | + | |
| 31 | +--- | |
| 32 | + | |
| 33 | +## Core Features | |
| 34 | + | |
| 35 | +### Must-Have (v1.0) | |
| 36 | + | |
| 37 | +1. **Real-time Treemap Visualization** | |
| 38 | + - Animated growth during scanning | |
| 39 | + - Nested rectangles proportional to file sizes | |
| 40 | + - Smooth layout transitions | |
| 41 | + | |
| 42 | +2. **Two Layout Algorithms** | |
| 43 | + - Squarified treemap (already implemented in sniffert) | |
| 44 | + - Cushioned treemap (3D shading effect like SpaceSniffer) | |
| 45 | + | |
| 46 | +3. **Interactive Navigation** | |
| 47 | + - Click to zoom into directories | |
| 48 | + - Breadcrumb navigation bar | |
| 49 | + - Context menus (right-click) | |
| 50 | + - Keyboard shortcuts (same as sniffert: q, c, d, arrows) | |
| 51 | + | |
| 52 | +4. **Search and Filter** | |
| 53 | + - Search by filename (regex support) | |
| 54 | + - Filter by file type, size, date | |
| 55 | + - Highlight matching files | |
| 56 | + | |
| 57 | +5. **Safe File Deletion** | |
| 58 | + - Delete with confirmation dialog | |
| 59 | + - Show full path and size | |
| 60 | + - Large deletions require typing name | |
| 61 | + | |
| 62 | +6. **Visual Customization** | |
| 63 | + - Multiple color schemes (Light, Dark, High Contrast) | |
| 64 | + - Color by file type, age, or custom | |
| 65 | + - Configurable fonts and sizes | |
| 66 | + | |
| 67 | +7. **High Performance** | |
| 68 | + - Handle 1M+ files | |
| 69 | + - 60fps rendering | |
| 70 | + - Background scanning without blocking UI | |
| 71 | + | |
| 72 | +### Nice-to-Have (Post-v1.0) | |
| 73 | + | |
| 74 | +- Export treemap as PNG/SVG | |
| 75 | +- Compare two directory snapshots | |
| 76 | +- Bookmarks for frequent directories | |
| 77 | +- Hidden files toggle | |
| 78 | +- Multiple tabs | |
| 79 | + | |
| 80 | +--- | |
| 81 | + | |
| 82 | +## Architecture Overview | |
| 83 | + | |
| 84 | +``` | |
| 85 | +┌─────────────────────────────────────────┐ | |
| 86 | +│ GTK4 Main Window │ | |
| 87 | +│ ┌───────────────────────────────────┐ │ | |
| 88 | +│ │ Toolbar: Scan | Layout | Search │ │ | |
| 89 | +│ ├───────────────────────────────────┤ │ | |
| 90 | +│ │ │ │ | |
| 91 | +│ │ Treemap Widget (Custom) │ │ | |
| 92 | +│ │ ┌───────────────────────────┐ │ │ | |
| 93 | +│ │ │ Cairo Drawing Surface │ │ │ | |
| 94 | +│ │ │ (Rectangles, gradients) │ │ │ | |
| 95 | +│ │ └───────────────────────────┘ │ │ | |
| 96 | +│ │ │ │ | |
| 97 | +│ ├───────────────────────────────────┤ │ | |
| 98 | +│ │ Status: Scanning /foo/bar │ │ | |
| 99 | +│ └───────────────────────────────────┘ │ | |
| 100 | +└─────────────────────────────────────────┘ | |
| 101 | + │ | |
| 102 | + ↓ | |
| 103 | +┌─────────────────────────────────────────┐ | |
| 104 | +│ Application State Manager │ | |
| 105 | +│ • Directory tree (file_node) │ | |
| 106 | +│ • Layout cache │ | |
| 107 | +│ • Selection state │ | |
| 108 | +│ • Scan progress │ | |
| 109 | +└─────────────────────────────────────────┘ | |
| 110 | + │ | |
| 111 | + ┌──────┴───────┐ | |
| 112 | + ↓ ↓ | |
| 113 | +┌────────┐ ┌────────┐ | |
| 114 | +│Scanner │ │Renderer│ | |
| 115 | +│(thread)│ │(Cairo) │ | |
| 116 | +└────────┘ └────────┘ | |
| 117 | +``` | |
| 118 | + | |
| 119 | +### Module Organization | |
| 120 | + | |
| 121 | +``` | |
| 122 | +src/ | |
| 123 | +├── core/ # Reused from sniffert | |
| 124 | +│ ├── types.f90 | |
| 125 | +│ ├── file_system.f90 | |
| 126 | +│ ├── disk_scanner.f90 | |
| 127 | +│ └── utils.f90 | |
| 128 | +│ | |
| 129 | +├── layout/ # Treemap algorithms | |
| 130 | +│ ├── squarified.f90 (port from sniffert) | |
| 131 | +│ ├── cushioned.f90 (new implementation) | |
| 132 | +│ └── layout_manager.f90 | |
| 133 | +│ | |
| 134 | +├── gui/ # GTK4 interface | |
| 135 | +│ ├── gtk_app.f90 | |
| 136 | +│ ├── main_window.f90 | |
| 137 | +│ ├── treemap_widget.f90 | |
| 138 | +│ └── dialogs.f90 | |
| 139 | +│ | |
| 140 | +├── rendering/ # Cairo drawing | |
| 141 | +│ ├── cairo_renderer.f90 | |
| 142 | +│ ├── colors.f90 | |
| 143 | +│ └── effects.f90 | |
| 144 | +│ | |
| 145 | +└── state/ # App state | |
| 146 | + ├── app_state.f90 | |
| 147 | + ├── selection.f90 | |
| 148 | + └── scan_manager.f90 | |
| 149 | +``` | |
| 150 | + | |
| 151 | +--- | |
| 152 | + | |
| 153 | +## Development Timeline | |
| 154 | + | |
| 155 | +**Total Duration:** 12-14 weeks to v1.0 | |
| 156 | + | |
| 157 | +| Phase | Weeks | Goal | Key Deliverables | | |
| 158 | +|-------|-------|------|-----------------| | |
| 159 | +| **Phase 1** | 1-2 | Foundation & GTK Setup | Basic GTK window, Cairo drawing | | |
| 160 | +| **Phase 2** | 3-4 | Core Data & Scanning | Background scanning, state management | | |
| 161 | +| **Phase 3** | 5-6 | Layout Algorithms | Squarified + Cushioned layouts | | |
| 162 | +| **Phase 4** | 7-8 | Cairo Rendering | Beautiful rendering, cushion effects | | |
| 163 | +| **Phase 5** | 9-10 | Interactivity | Navigation, context menus, keyboard | | |
| 164 | +| **Phase 6** | 11 | Search & Filter | Search box, filtering UI | | |
| 165 | +| **Phase 7** | 12 | Polish & Config | Settings, animations, accessibility | | |
| 166 | +| **Phase 8** | 13-14 | Testing & Packaging | Packages for macOS/Linux | | |
| 167 | + | |
| 168 | +### Current Status | |
| 169 | + | |
| 170 | +✅ **Planning Complete** (Week 0) | |
| 171 | +- All documentation written | |
| 172 | +- Architecture designed | |
| 173 | +- Stack chosen and validated | |
| 174 | + | |
| 175 | +🚧 **Phase 1 Starting** (Week 1) | |
| 176 | +- Install dependencies | |
| 177 | +- Build hello world GTK app | |
| 178 | +- Set up project structure | |
| 179 | + | |
| 180 | +--- | |
| 181 | + | |
| 182 | +## Key Technical Decisions | |
| 183 | + | |
| 184 | +### 1. Pure Fortran (No C++/Python hybrid) | |
| 185 | + | |
| 186 | +**Decision:** Use ONLY Fortran via gtk-fortran | |
| 187 | +**Rationale:** Proves the point that Fortran can do GUIs | |
| 188 | +**Trade-off:** Steeper learning curve, less examples | |
| 189 | +**Mitigation:** gtk-fortran is mature and well-documented | |
| 190 | + | |
| 191 | +### 2. GTK4 (Not Qt, wxWidgets, or custom OpenGL) | |
| 192 | + | |
| 193 | +**Decision:** GTK4 via gtk-fortran | |
| 194 | +**Rationale:** | |
| 195 | +- Only mature Fortran GUI bindings exist | |
| 196 | +- Native on Linux, good on macOS (Cocoa backend) | |
| 197 | +- Cairo integration for custom drawing | |
| 198 | + | |
| 199 | +**Trade-off:** Not as native-looking on macOS as Qt | |
| 200 | +**Mitigation:** GTK4 is much better than GTK3 on macOS | |
| 201 | + | |
| 202 | +### 3. Cushioned Treemap (Not Spiral) | |
| 203 | + | |
| 204 | +**Decision:** Implement cushioned treemap (like SpaceSniffer) | |
| 205 | +**Rationale:** Research shows SpaceSniffer uses cushioned, not spiral | |
| 206 | +**Implementation:** Van Wijk algorithm with Cairo gradients | |
| 207 | +**Benefit:** More visually appealing than flat squarified | |
| 208 | + | |
| 209 | +### 4. Meson Build System (Not Make or pure FPM) | |
| 210 | + | |
| 211 | +**Decision:** Meson primary, FPM fallback | |
| 212 | +**Rationale:** | |
| 213 | +- Meson is GTK standard | |
| 214 | +- Handles pkg-config dependencies elegantly | |
| 215 | +- Cross-platform without pain | |
| 216 | + | |
| 217 | +**Trade-off:** Fortran community less familiar with Meson | |
| 218 | +**Mitigation:** Provide clear build instructions | |
| 219 | + | |
| 220 | +### 5. Package Manager Distribution (Not just GitHub releases) | |
| 221 | + | |
| 222 | +**Decision:** Target Homebrew, apt, pacman | |
| 223 | +**Rationale:** Users expect `brew install sniffly`, not "build from source" | |
| 224 | +**Implementation:** Create packages in Phase 8 | |
| 225 | +**Benefit:** Wider adoption, professional image | |
| 226 | + | |
| 227 | +--- | |
| 228 | + | |
| 229 | +## Success Metrics | |
| 230 | + | |
| 231 | +### Technical Success | |
| 232 | + | |
| 233 | +- ✅ Builds on macOS and Linux without errors | |
| 234 | +- ✅ Handles 1M+ files in < 10 seconds scan time | |
| 235 | +- ✅ Renders at 60fps with 1000s of visible nodes | |
| 236 | +- ✅ Uses < 1GB RAM for 1M files | |
| 237 | +- ✅ No memory leaks (valgrind clean) | |
| 238 | + | |
| 239 | +### Community Success | |
| 240 | + | |
| 241 | +- 🎯 1,000 GitHub stars in first 3 months | |
| 242 | +- 🎯 10,000 downloads in first 6 months | |
| 243 | +- 🎯 Accepted into Homebrew and major Linux repos | |
| 244 | +- 🎯 5+ external contributors | |
| 245 | +- 🎯 Featured on Hacker News or /r/programming | |
| 246 | + | |
| 247 | +### Philosophy Success | |
| 248 | + | |
| 249 | +- 🎯 Proves Fortran can build modern GUIs | |
| 250 | +- 🎯 Becomes showcase example for gtk-fortran | |
| 251 | +- 🎯 Inspires other Fortran GUI projects | |
| 252 | +- 🎯 Mentioned in "Fortran can do that?!" articles | |
| 253 | + | |
| 254 | +--- | |
| 255 | + | |
| 256 | +## Risk Assessment | |
| 257 | + | |
| 258 | +| Risk | Probability | Impact | Mitigation | | |
| 259 | +|------|------------|--------|------------| | |
| 260 | +| gtk-fortran bugs/limitations | Medium | High | Join community early, contribute fixes | | |
| 261 | +| Performance insufficient | Low | High | Profile early, optimize, viewport culling | | |
| 262 | +| GTK4 not native on macOS | Low | Medium | Accept "good enough", focus on functionality | | |
| 263 | +| Build complexity scares users | Medium | Medium | Package managers, pre-built binaries | | |
| 264 | +| Cushion algorithm too complex | Low | Low | Well-documented (Van Wijk paper), examples exist | | |
| 265 | +| Timeline slips | Medium | Low | Phases are independent, can adjust scope | | |
| 266 | + | |
| 267 | +--- | |
| 268 | + | |
| 269 | +## Documentation Created | |
| 270 | + | |
| 271 | +All planning documents are complete and ready: | |
| 272 | + | |
| 273 | +1. **README.md** - Project overview, status, quick links | |
| 274 | +2. **SNIFFLY_MASTER_PLAN.md** - Complete 14-week roadmap (comprehensive!) | |
| 275 | +3. **TECHNICAL_STACK.md** - Deep dive on all technology choices | |
| 276 | +4. **QUICKSTART.md** - 30-minute setup guide with examples | |
| 277 | +5. **GETTING_STARTED.md** - Contributor guide, workflow, style | |
| 278 | +6. **PROJECT_SUMMARY.md** - This document | |
| 279 | +7. **LICENSE** - MIT License | |
| 280 | +8. **meson.build** - Build configuration | |
| 281 | +9. **.gitignore** - Git ignore rules | |
| 282 | + | |
| 283 | +**Total Documentation:** ~15,000 words across 9 files | |
| 284 | + | |
| 285 | +--- | |
| 286 | + | |
| 287 | +## Project Structure Created | |
| 288 | + | |
| 289 | +``` | |
| 290 | +sniffly/ | |
| 291 | +├── README.md | |
| 292 | +├── SNIFFLY_MASTER_PLAN.md | |
| 293 | +├── TECHNICAL_STACK.md | |
| 294 | +├── QUICKSTART.md | |
| 295 | +├── GETTING_STARTED.md | |
| 296 | +├── PROJECT_SUMMARY.md | |
| 297 | +├── LICENSE | |
| 298 | +├── meson.build | |
| 299 | +├── .gitignore | |
| 300 | +│ | |
| 301 | +├── app/ # (empty, ready for main.f90) | |
| 302 | +├── src/ | |
| 303 | +│ ├── core/ # (empty, ready for modules) | |
| 304 | +│ ├── layout/ | |
| 305 | +│ ├── gui/ | |
| 306 | +│ ├── rendering/ | |
| 307 | +│ └── state/ | |
| 308 | +├── test/ # (empty, ready for tests) | |
| 309 | +├── docs/ # (empty, ready for docs) | |
| 310 | +└── examples/ # (empty, ready for examples) | |
| 311 | +``` | |
| 312 | + | |
| 313 | +--- | |
| 314 | + | |
| 315 | +## Next Steps (Immediate) | |
| 316 | + | |
| 317 | +### Week 1: Environment Setup | |
| 318 | + | |
| 319 | +1. **Install Dependencies** | |
| 320 | + ```bash | |
| 321 | + # macOS | |
| 322 | + brew install gtk4 gfortran meson ninja | |
| 323 | + | |
| 324 | + # Ubuntu | |
| 325 | + sudo apt install libgtk-4-dev gfortran meson ninja-build | |
| 326 | + ``` | |
| 327 | + | |
| 328 | +2. **Build gtk-fortran** | |
| 329 | + ```bash | |
| 330 | + git clone https://github.com/vmagnin/gtk-fortran.git | |
| 331 | + cd gtk-fortran | |
| 332 | + cmake -B build && cmake --build build | |
| 333 | + sudo cmake --install build | |
| 334 | + ``` | |
| 335 | + | |
| 336 | +3. **Test GTK4 Works** | |
| 337 | + - Follow QUICKSTART.md hello world example | |
| 338 | + - Verify window opens | |
| 339 | + - Verify Cairo drawing works | |
| 340 | + | |
| 341 | +4. **Create First Modules** | |
| 342 | + - Port `src/types.f90` from sniffert | |
| 343 | + - Port `src/file_system.f90` from sniffert | |
| 344 | + - Create `app/main.f90` with basic GTK app | |
| 345 | + | |
| 346 | +### Week 2: Basic UI | |
| 347 | + | |
| 348 | +1. **Create gtk_app Module** | |
| 349 | + - Initialize GTK application | |
| 350 | + - Create main window | |
| 351 | + - Add menu bar skeleton | |
| 352 | + | |
| 353 | +2. **Create treemap_widget Module** | |
| 354 | + - Custom GtkDrawingArea | |
| 355 | + - Connect draw signal | |
| 356 | + - Draw static rectangles (test) | |
| 357 | + | |
| 358 | +3. **Test Build System** | |
| 359 | + - Update meson.build with sources | |
| 360 | + - Build and run | |
| 361 | + - Verify end-to-end compilation | |
| 362 | + | |
| 363 | +--- | |
| 364 | + | |
| 365 | +## Long-term Vision | |
| 366 | + | |
| 367 | +### Version 1.0 (Week 14) | |
| 368 | + | |
| 369 | +- ✅ Feature parity with terminal sniffert | |
| 370 | +- ✅ Beautiful GUI with cushion effects | |
| 371 | +- ✅ Packages for macOS and Linux | |
| 372 | +- ✅ Documentation complete | |
| 373 | + | |
| 374 | +### Version 1.x (Months 2-6) | |
| 375 | + | |
| 376 | +- Multiple tabs for different scans | |
| 377 | +- Export treemap as images | |
| 378 | +- Compare directory snapshots | |
| 379 | +- Cloud storage integration (?) | |
| 380 | + | |
| 381 | +### Version 2.0 (6+ months) | |
| 382 | + | |
| 383 | +- Plugin system for custom layouts | |
| 384 | +- Network drive scanning | |
| 385 | +- Advanced filtering (by owner, permissions) | |
| 386 | +- Duplicate file detection | |
| 387 | + | |
| 388 | +--- | |
| 389 | + | |
| 390 | +## Relationship to sniffert | |
| 391 | + | |
| 392 | +Sniffly is the GUI companion to sniffert: | |
| 393 | + | |
| 394 | +| Feature | sniffert (terminal) | sniffly (GUI) | | |
| 395 | +|---------|-------------------|---------------| | |
| 396 | +| **Interface** | Terminal (ncurses) | Native window (GTK4) | | |
| 397 | +| **Input** | Keyboard only | Mouse + keyboard | | |
| 398 | +| **Visual** | ASCII art | Beautiful cushion maps | | |
| 399 | +| **Performance** | Lightweight | More resource-intensive | | |
| 400 | +| **Use Case** | SSH, servers | Desktop workstations | | |
| 401 | +| **Shared Code** | Types, scanning, layout algorithms | | | |
| 402 | + | |
| 403 | +Both prove Fortran can do modern UIs! | |
| 404 | + | |
| 405 | +--- | |
| 406 | + | |
| 407 | +## Why This Will Succeed | |
| 408 | + | |
| 409 | +1. **Solid Foundation** | |
| 410 | + - Proven algorithms from sniffert | |
| 411 | + - gtk-fortran is mature and maintained | |
| 412 | + - GTK4 is modern and well-documented | |
| 413 | + | |
| 414 | +2. **Clear Plan** | |
| 415 | + - 8 phases with specific deliverables | |
| 416 | + - Each phase builds on previous | |
| 417 | + - Realistic timeline with buffer | |
| 418 | + | |
| 419 | +3. **Technical Excellence** | |
| 420 | + - Performance targets defined | |
| 421 | + - Testing strategy planned | |
| 422 | + - Package distribution planned | |
| 423 | + | |
| 424 | +4. **Community Appeal** | |
| 425 | + - "Fortran can do that?!" factor | |
| 426 | + - Useful tool people actually need | |
| 427 | + - Open source, easy to contribute | |
| 428 | + | |
| 429 | +5. **Proven Demand** | |
| 430 | + - SpaceSniffer is popular on Windows | |
| 431 | + - Unix users want equivalent | |
| 432 | + - No good open-source alternative exists | |
| 433 | + | |
| 434 | +--- | |
| 435 | + | |
| 436 | +## Quotes to Remember | |
| 437 | + | |
| 438 | +> "Why Fortran?" | |
| 439 | +> **"Why not? It's fast, proven, and this proves it can do anything."** | |
| 440 | + | |
| 441 | +> "Is gtk-fortran mature enough?" | |
| 442 | +> **"Yes! It's been around since 2011, actively maintained, and production-ready."** | |
| 443 | + | |
| 444 | +> "Can Fortran really do GUIs?" | |
| 445 | +> **"Watch us. We're building a SpaceSniffer clone that will blow your mind."** | |
| 446 | + | |
| 447 | +--- | |
| 448 | + | |
| 449 | +## Final Checklist | |
| 450 | + | |
| 451 | +Planning Phase Complete: | |
| 452 | + | |
| 453 | +- ✅ Research SpaceSniffer features | |
| 454 | +- ✅ Research gtk-fortran capabilities | |
| 455 | +- ✅ Choose technology stack | |
| 456 | +- ✅ Design architecture | |
| 457 | +- ✅ Create module structure | |
| 458 | +- ✅ Write comprehensive documentation | |
| 459 | +- ✅ Create project structure | |
| 460 | +- ✅ Define success metrics | |
| 461 | +- ✅ Assess risks | |
| 462 | +- ✅ Create timeline | |
| 463 | + | |
| 464 | +**Status: Ready to Begin Development! 🚀** | |
| 465 | + | |
| 466 | +--- | |
| 467 | + | |
| 468 | +## Contact & Links | |
| 469 | + | |
| 470 | +- **Project Location:** `/Users/matthewwolffe/Documents/GithubOrgs/FortranGoingOnForty/sniffly` | |
| 471 | +- **Related Project:** `../sniffert` (terminal version) | |
| 472 | +- **Main Documentation:** `SNIFFLY_MASTER_PLAN.md` | |
| 473 | +- **Quick Start:** `QUICKSTART.md` | |
| 474 | + | |
| 475 | +--- | |
| 476 | + | |
| 477 | +**Sniffly: Proving Fortran Can Build Beautiful GUIs** | |
| 478 | + | |
| 479 | +*Created: 2025-11-04* | |
| 480 | +*Planning Phase: Complete ✅* | |
| 481 | +*Next Phase: Development Begins 🚀* | |
SNIFFLY-TABS.mdadded@@ -0,0 +1,22 @@ | ||
| 1 | +# tabs in sniffly | |
| 2 | + | |
| 3 | +one thing that spacesniffer had that we dont is control of some kind of multi directory view | |
| 4 | + | |
| 5 | +spacesniffer achieved this with something akin to panes | |
| 6 | + - if i recall it was actually sub windows within the main window | |
| 7 | + | |
| 8 | +I have a slightly different vision. | |
| 9 | +I always found the panes approach to be cluttered. | |
| 10 | + | |
| 11 | +We have a natural space for a tab bar. It's on the far right on the same level as the breadcrumn | |
| 12 | +they'll be small to fit in the space but they'll have active highlighting with a yellowish border and maybe slight highlighting | |
| 13 | +you'll be able to navigate between tabs with cmd-<number key> | |
| 14 | +tabs will have small x's on them to close | |
| 15 | +tabs open to the left, that is there's a small + button on the left side of the most recently made tab | |
| 16 | +the program starts in tab 0 (alt-1) by default. | |
| 17 | + | |
| 18 | +we'll have to be careful about state. | |
| 19 | +We'll have to be careful about scans in quicl succession or concurrent scans since that will require juggling many threads unless we can be smart about it and be clean and safe. | |
| 20 | + | |
| 21 | +what other pitfalls do you see? Let's discuss and plan this feature' | |
| 22 | + | |
SNIFFLY_MASTER_PLAN.mdadded@@ -0,0 +1,1101 @@ | ||
| 1 | +# SNIFFLY - Pure Fortran GUI Disk Analyzer | |
| 2 | +## The SpaceSniffer Clone That Proves Fortran Can Do Anything | |
| 3 | + | |
| 4 | +--- | |
| 5 | + | |
| 6 | +## 🎯 Project Vision | |
| 7 | + | |
| 8 | +Build a near 1:1 clone of SpaceSniffer using **pure Fortran** with GTK4, proving that Fortran can create modern, performant GUI applications that rival commercial Windows software. Target macOS and Linux with package manager distribution. | |
| 9 | + | |
| 10 | +--- | |
| 11 | + | |
| 12 | +## 📋 Executive Summary | |
| 13 | + | |
| 14 | +**What:** Native GUI disk analyzer with real-time treemap visualization | |
| 15 | +**Why:** Prove Fortran's capabilities while providing Unix users with a SpaceSniffer alternative | |
| 16 | +**How:** gtk-fortran + GTK4 + Cairo for rendering, leveraging sniffert's proven algorithms | |
| 17 | +**When:** 12-week development cycle with 4 major milestones | |
| 18 | +**Success:** Native performance, handles 1M+ files, packaged for Homebrew/apt/pacman | |
| 19 | + | |
| 20 | +--- | |
| 21 | + | |
| 22 | +## 🏗️ Technology Stack | |
| 23 | + | |
| 24 | +### Core Technologies | |
| 25 | + | |
| 26 | +| Component | Technology | Rationale | | |
| 27 | +|-----------|-----------|-----------| | |
| 28 | +| **Language** | Fortran 2008+ (gfortran 9.0+) | Pure Fortran philosophy, proven performance | | |
| 29 | +| **GUI Framework** | GTK4 via gtk-fortran | Modern, cross-platform, well-maintained | | |
| 30 | +| **Graphics** | Cairo 1.18+ | Hardware-accelerated 2D drawing | | |
| 31 | +| **Build System** | Meson + FPM fallback | GTK standard, Fortran-friendly | | |
| 32 | +| **Dependencies** | GLib 2.80+, GDK4, Pango | GTK ecosystem | | |
| 33 | + | |
| 34 | +### Why gtk-fortran? | |
| 35 | + | |
| 36 | +1. **Full GTK4 Support**: Latest gtk-fortran (3.24.41) includes GTK4 bindings | |
| 37 | +2. **Cairo Integration**: Direct access to Cairo drawing API for custom rendering | |
| 38 | +3. **Cross-Platform**: Linux, macOS, FreeBSD native support | |
| 39 | +4. **Active Development**: Maintained by Vincent Magnin, regular updates | |
| 40 | +5. **Fortran-Native**: High-level interface using Fortran optional arguments | |
| 41 | +6. **Production-Ready**: Used in scientific visualization applications | |
| 42 | + | |
| 43 | +--- | |
| 44 | + | |
| 45 | +## 🎨 SpaceSniffer Feature Analysis | |
| 46 | + | |
| 47 | +### Core Features (Must-Have) | |
| 48 | + | |
| 49 | +1. **Real-time Treemap Visualization** | |
| 50 | + - Animated growth as scanning progresses | |
| 51 | + - Nested rectangles proportional to file sizes | |
| 52 | + - Smooth transitions on layout changes | |
| 53 | + | |
| 54 | +2. **Interactive Navigation** | |
| 55 | + - Click to zoom into directories | |
| 56 | + - Breadcrumb navigation bar | |
| 57 | + - Right-click context menus | |
| 58 | + - Keyboard shortcuts (same as sniffert) | |
| 59 | + | |
| 60 | +3. **Visual Enhancements** | |
| 61 | + - Cushioned treemap effect (3D shading) | |
| 62 | + - Color coding by file type/age | |
| 63 | + - Smart label placement (only when space permits) | |
| 64 | + - Hover tooltips with full path and details | |
| 65 | + | |
| 66 | +4. **Scanning Features** | |
| 67 | + - Background scanning with progress indicator | |
| 68 | + - Pause/resume scanning | |
| 69 | + - Skip inaccessible directories gracefully | |
| 70 | + - Show free space and unknown space | |
| 71 | + | |
| 72 | +5. **File Management** | |
| 73 | + - Open files/directories in default app | |
| 74 | + - Delete with confirmation dialog | |
| 75 | + - Show in file manager | |
| 76 | + - Copy path to clipboard | |
| 77 | + | |
| 78 | +6. **Search and Filter** | |
| 79 | + - Text search by filename | |
| 80 | + - Filter by file type (regex patterns) | |
| 81 | + - Filter by size range | |
| 82 | + - Filter by date range | |
| 83 | + | |
| 84 | +7. **Configuration** | |
| 85 | + - Layout algorithm selection (Squarified/Cushioned) | |
| 86 | + - Color scheme customization | |
| 87 | + - Animation speed control | |
| 88 | + - Font size adjustment | |
| 89 | + | |
| 90 | +### Optional Features (Nice-to-Have) | |
| 91 | + | |
| 92 | +- Export treemap as PNG/SVG | |
| 93 | +- Compare two directory snapshots | |
| 94 | +- Bookmarks for frequent directories | |
| 95 | +- Hidden files toggle | |
| 96 | +- Multiple tabs for different scans | |
| 97 | + | |
| 98 | +--- | |
| 99 | + | |
| 100 | +## 📐 Architecture Design | |
| 101 | + | |
| 102 | +### Module Structure | |
| 103 | + | |
| 104 | +``` | |
| 105 | +sniffly/ | |
| 106 | +├── meson.build # Primary build system | |
| 107 | +├── fpm.toml # Fallback build system | |
| 108 | +├── app/ | |
| 109 | +│ └── main.f90 # GTK application entry point | |
| 110 | +├── src/ | |
| 111 | +│ ├── core/ # Reusable from sniffert | |
| 112 | +│ │ ├── types.f90 # Core data types (file_node, rect) | |
| 113 | +│ │ ├── file_system.f90 # POSIX filesystem operations | |
| 114 | +│ │ ├── disk_scanner.f90 # Background scanning with callbacks | |
| 115 | +│ │ └── utils.f90 # String formatting, size conversion | |
| 116 | +│ │ | |
| 117 | +│ ├── layout/ # Treemap algorithms | |
| 118 | +│ │ ├── squarified.f90 # Port from sniffert (enhanced) | |
| 119 | +│ │ ├── cushioned.f90 # Cushion treemap with shading | |
| 120 | +│ │ └── layout_manager.f90 # Algorithm selection/switching | |
| 121 | +│ │ | |
| 122 | +│ ├── gui/ # GTK4 interface | |
| 123 | +│ │ ├── gtk_app.f90 # GtkApplication setup | |
| 124 | +│ │ ├── main_window.f90 # Main application window | |
| 125 | +│ │ ├── treemap_widget.f90 # Custom GtkDrawingArea widget | |
| 126 | +│ │ ├── toolbar.f90 # Top toolbar with controls | |
| 127 | +│ │ ├── statusbar.f90 # Bottom status bar | |
| 128 | +│ │ ├── dialogs.f90 # Confirmation, settings dialogs | |
| 129 | +│ │ └── menus.f90 # Context menus, menu bar | |
| 130 | +│ │ | |
| 131 | +│ ├── rendering/ # Cairo drawing | |
| 132 | +│ │ ├── cairo_renderer.f90 # Core rendering engine | |
| 133 | +│ │ ├── colors.f90 # Color schemes and gradients | |
| 134 | +│ │ ├── text_layout.f90 # Pango text rendering | |
| 135 | +│ │ └── effects.f90 # Cushion shading, animations | |
| 136 | +│ │ | |
| 137 | +│ └── state/ # Application state | |
| 138 | +│ ├── app_state.f90 # Global state management | |
| 139 | +│ ├── selection.f90 # Selection and navigation | |
| 140 | +│ ├── config.f90 # User settings persistence | |
| 141 | +│ └── scan_manager.f90 # Background scan coordination | |
| 142 | +│ | |
| 143 | +└── test/ | |
| 144 | + ├── test_layouts.f90 # Layout algorithm tests | |
| 145 | + ├── test_rendering.f90 # Rendering tests | |
| 146 | + └── test_gui.f90 # GUI interaction tests | |
| 147 | +``` | |
| 148 | + | |
| 149 | +### Data Flow Architecture | |
| 150 | + | |
| 151 | +``` | |
| 152 | +┌─────────────────────────────────────────────────────────┐ | |
| 153 | +│ GTK4 Main Loop │ | |
| 154 | +│ (Event Processing) │ | |
| 155 | +└─────────────────────────────────────────────────────────┘ | |
| 156 | + │ | |
| 157 | + ↓ | |
| 158 | +┌─────────────────────────────────────────────────────────┐ | |
| 159 | +│ Main Window (GTK) │ | |
| 160 | +│ ┌─────────────────────────────────────────────────┐ │ | |
| 161 | +│ │ Toolbar: Scan Path | Layout | Color | Search │ │ | |
| 162 | +│ └─────────────────────────────────────────────────┘ │ | |
| 163 | +│ ┌─────────────────────────────────────────────────┐ │ | |
| 164 | +│ │ Treemap Widget (GtkDrawingArea) │ │ | |
| 165 | +│ │ │ │ | |
| 166 | +│ │ ┌────────────────────────────────────────┐ │ │ | |
| 167 | +│ │ │ Cairo Drawing Surface │ │ │ | |
| 168 | +│ │ │ (Custom rendering via ::draw signal) │ │ │ | |
| 169 | +│ │ └────────────────────────────────────────┘ │ │ | |
| 170 | +│ └─────────────────────────────────────────────────┘ │ | |
| 171 | +│ ┌─────────────────────────────────────────────────┐ │ | |
| 172 | +│ │ Statusbar: Scanning... | Selected: /foo/bar │ │ | |
| 173 | +│ └─────────────────────────────────────────────────┘ │ | |
| 174 | +└─────────────────────────────────────────────────────────┘ | |
| 175 | + │ | |
| 176 | + ↓ | |
| 177 | +┌─────────────────────────────────────────────────────────┐ | |
| 178 | +│ Application State Manager │ | |
| 179 | +│ • Current directory tree (file_node) │ | |
| 180 | +│ • Layout cache (cached bounds) │ | |
| 181 | +│ • Selection state (current node, breadcrumb) │ | |
| 182 | +│ • Scan progress (% complete, items scanned) │ | |
| 183 | +└─────────────────────────────────────────────────────────┘ | |
| 184 | + │ | |
| 185 | + ┌──────────────────┼──────────────────┐ | |
| 186 | + ↓ ↓ ↓ | |
| 187 | +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ | |
| 188 | +│Disk Scanner │ │Layout Engine │ │Cairo Renderer│ | |
| 189 | +│(Background │ │(Squarified/ │ │(Drawing API) │ | |
| 190 | +│ Thread) │ │ Cushioned) │ │ │ | |
| 191 | +└──────────────┘ └──────────────┘ └──────────────┘ | |
| 192 | +``` | |
| 193 | + | |
| 194 | +### Threading Model | |
| 195 | + | |
| 196 | +1. **Main Thread (GTK Event Loop)** | |
| 197 | + - Handle all GUI events | |
| 198 | + - Process user input | |
| 199 | + - Trigger redraws (gtk_widget_queue_draw) | |
| 200 | + | |
| 201 | +2. **Scanning Thread (GLib Thread)** | |
| 202 | + - Background directory traversal | |
| 203 | + - Periodic callbacks to main thread with updates | |
| 204 | + - Use g_idle_add for thread-safe UI updates | |
| 205 | + | |
| 206 | +3. **Layout Calculation** | |
| 207 | + - Triggered by scan updates or window resize | |
| 208 | + - Can be deferred if user is interacting | |
| 209 | + - Cache results to avoid recalculation | |
| 210 | + | |
| 211 | +--- | |
| 212 | + | |
| 213 | +## 🎨 Treemap Algorithms | |
| 214 | + | |
| 215 | +### 1. Squarified Treemap (Already Implemented!) | |
| 216 | + | |
| 217 | +**Status:** Port from sniffert with minor enhancements | |
| 218 | +**Algorithm:** Bruls et al. 2000 | |
| 219 | +**Advantages:** | |
| 220 | +- Excellent aspect ratios (more square rectangles) | |
| 221 | +- Easy to compare sizes at a glance | |
| 222 | +- Already proven in sniffert | |
| 223 | + | |
| 224 | +**Implementation Notes:** | |
| 225 | +- Reuse existing squarified.f90 from sniffert | |
| 226 | +- Enhance with real-time updates (incremental layout) | |
| 227 | +- Add smooth animation between layout changes | |
| 228 | + | |
| 229 | +### 2. Cushioned Treemap (New Implementation) | |
| 230 | + | |
| 231 | +**Status:** New development | |
| 232 | +**Algorithm:** Van Wijk & Van de Wetering 1999 | |
| 233 | +**Visual Effect:** Adds 3D shading to rectangles using bump mapping | |
| 234 | + | |
| 235 | +**How Cushion Shading Works:** | |
| 236 | + | |
| 237 | +1. **Recursive Surface Function** | |
| 238 | + ``` | |
| 239 | + For each rectangle at depth d: | |
| 240 | + - Base surface: f(x, y) = 0 | |
| 241 | + - Add height function: h(x, y) = ax² + by² | |
| 242 | + - Parameters a, b decrease with depth | |
| 243 | + ``` | |
| 244 | + | |
| 245 | +2. **Lighting Model** | |
| 246 | + ``` | |
| 247 | + - Light source direction: L = (lx, ly, lz) | |
| 248 | + - Surface normal: N = (-∂f/∂x, -∂f/∂y, 1) | |
| 249 | + - Color intensity: I = ambient + diffuse * (N · L) | |
| 250 | + ``` | |
| 251 | + | |
| 252 | +3. **Implementation Steps** | |
| 253 | + - Calculate cushion parameters for each node | |
| 254 | + - Store in enhanced file_node structure | |
| 255 | + - Apply during Cairo rendering pass | |
| 256 | + | |
| 257 | +**Rendering in Cairo:** | |
| 258 | +```fortran | |
| 259 | +! For each cushioned rectangle: | |
| 260 | +! 1. Create linear gradient based on cushion function | |
| 261 | +! 2. Apply gradient as fill pattern | |
| 262 | +! 3. Add border for clarity | |
| 263 | +cairo_pattern_create_linear(x1, y1, x2, y2) | |
| 264 | +cairo_pattern_add_color_stop_rgba(pattern, 0.0, r1, g1, b1, 1.0) | |
| 265 | +cairo_pattern_add_color_stop_rgba(pattern, 1.0, r2, g2, b2, 1.0) | |
| 266 | +cairo_set_source(cr, pattern) | |
| 267 | +cairo_rectangle(cr, x, y, w, h) | |
| 268 | +cairo_fill(cr) | |
| 269 | +``` | |
| 270 | + | |
| 271 | +--- | |
| 272 | + | |
| 273 | +## 🎯 Implementation Phases | |
| 274 | + | |
| 275 | +### Phase 1: Foundation & GTK Setup (Weeks 1-2) | |
| 276 | + | |
| 277 | +**Goal:** Get GTK4 + gtk-fortran working with basic window | |
| 278 | + | |
| 279 | +#### Tasks: | |
| 280 | +1. **Environment Setup** | |
| 281 | + - Install GTK4 development libraries (macOS: Homebrew, Linux: package manager) | |
| 282 | + - Install gtk-fortran from source or package | |
| 283 | + - Create Meson build configuration | |
| 284 | + - Test minimal GTK app (Hello World window) | |
| 285 | + | |
| 286 | +2. **Project Structure** | |
| 287 | + - Create module skeleton | |
| 288 | + - Set up git repository structure | |
| 289 | + - Configure build system (Meson + FPM fallback) | |
| 290 | + - Create basic CI/CD workflow | |
| 291 | + | |
| 292 | +3. **Basic GTK Application** | |
| 293 | + - Implement GtkApplication setup | |
| 294 | + - Create main window with title bar | |
| 295 | + - Add basic menu bar (File → Scan, Quit) | |
| 296 | + - Test on both macOS and Linux | |
| 297 | + | |
| 298 | +4. **GtkDrawingArea Integration** | |
| 299 | + - Create custom treemap widget | |
| 300 | + - Implement ::draw signal handler | |
| 301 | + - Test basic Cairo drawing (draw rectangle, text) | |
| 302 | + - Verify rendering performance (target 60fps) | |
| 303 | + | |
| 304 | +**Success Criteria:** | |
| 305 | +- Window opens with custom drawing area | |
| 306 | +- Can draw colored rectangles and text | |
| 307 | +- Builds on macOS and Linux | |
| 308 | +- No memory leaks (valgrind clean) | |
| 309 | + | |
| 310 | +**Estimated Time:** 2 weeks | |
| 311 | + | |
| 312 | +--- | |
| 313 | + | |
| 314 | +### Phase 2: Core Data & Scanning (Weeks 3-4) | |
| 315 | + | |
| 316 | +**Goal:** Port sniffert's scanning logic with GTK integration | |
| 317 | + | |
| 318 | +#### Tasks: | |
| 319 | +1. **Port Core Modules** | |
| 320 | + - Adapt types.f90 for GUI needs | |
| 321 | + - Port file_system.f90 unchanged | |
| 322 | + - Port disk_scanner.f90 with callback support | |
| 323 | + - Add thread-safe state management | |
| 324 | + | |
| 325 | +2. **Background Scanning** | |
| 326 | + - Implement GLib threading wrapper | |
| 327 | + - Create scan_manager for coordinating scans | |
| 328 | + - Add progress callbacks to main thread | |
| 329 | + - Test with various directory sizes | |
| 330 | + | |
| 331 | +3. **Progress UI** | |
| 332 | + - Add progress bar to main window | |
| 333 | + - Show scan statistics (files scanned, total size) | |
| 334 | + - Implement pause/resume/cancel controls | |
| 335 | + - Add estimated time remaining | |
| 336 | + | |
| 337 | +4. **State Management** | |
| 338 | + - Implement app_state module | |
| 339 | + - Track current directory tree | |
| 340 | + - Handle scan updates without blocking UI | |
| 341 | + - Add basic error handling | |
| 342 | + | |
| 343 | +**Success Criteria:** | |
| 344 | +- Can scan /tmp without blocking GUI | |
| 345 | +- Progress bar updates in real-time | |
| 346 | +- Can cancel mid-scan cleanly | |
| 347 | +- No crashes on permission denied | |
| 348 | + | |
| 349 | +**Estimated Time:** 2 weeks | |
| 350 | + | |
| 351 | +--- | |
| 352 | + | |
| 353 | +### Phase 3: Layout Algorithms (Weeks 5-6) | |
| 354 | + | |
| 355 | +**Goal:** Implement both squarified and cushioned layouts | |
| 356 | + | |
| 357 | +#### Tasks: | |
| 358 | +1. **Port Squarified Algorithm** | |
| 359 | + - Copy squarified.f90 from sniffert | |
| 360 | + - Adapt for real-time updates (incremental) | |
| 361 | + - Optimize for large datasets (1M+ nodes) | |
| 362 | + - Add unit tests comparing to sniffert output | |
| 363 | + | |
| 364 | +2. **Implement Cushioned Algorithm** | |
| 365 | + - Research cushion function parameters | |
| 366 | + - Implement recursive cushion calculation | |
| 367 | + - Store cushion data in file_node | |
| 368 | + - Add toggle between layouts | |
| 369 | + | |
| 370 | +3. **Layout Manager** | |
| 371 | + - Abstract layout selection | |
| 372 | + - Cache layout results | |
| 373 | + - Invalidate cache on window resize | |
| 374 | + - Implement smart recalculation (only changed subtrees) | |
| 375 | + | |
| 376 | +4. **Performance Optimization** | |
| 377 | + - Profile layout calculation time | |
| 378 | + - Target < 100ms for 10k nodes | |
| 379 | + - Use lazy evaluation for deep trees | |
| 380 | + - Consider viewport culling for large trees | |
| 381 | + | |
| 382 | +**Success Criteria:** | |
| 383 | +- Both layouts produce visually correct treemaps | |
| 384 | +- Can switch between layouts in < 1 second | |
| 385 | +- Handles 100k files without lag | |
| 386 | +- Layout respects window size constraints | |
| 387 | + | |
| 388 | +**Estimated Time:** 2 weeks | |
| 389 | + | |
| 390 | +--- | |
| 391 | + | |
| 392 | +### Phase 4: Cairo Rendering (Weeks 7-8) | |
| 393 | + | |
| 394 | +**Goal:** Beautiful, performant rendering with cushion effects | |
| 395 | + | |
| 396 | +#### Tasks: | |
| 397 | +1. **Basic Treemap Rendering** | |
| 398 | + - Implement cairo_renderer module | |
| 399 | + - Draw rectangles with borders | |
| 400 | + - Render text labels (using Pango) | |
| 401 | + - Handle clipping for nested rectangles | |
| 402 | + | |
| 403 | +2. **Cushion Shading** | |
| 404 | + - Implement lighting calculations | |
| 405 | + - Create Cairo gradients for cushions | |
| 406 | + - Add depth-based color variation | |
| 407 | + - Test different light angles | |
| 408 | + | |
| 409 | +3. **Color Schemes** | |
| 410 | + - Implement file type coloring (directories, files, media, code, etc.) | |
| 411 | + - Add color by age (old files, recent files) | |
| 412 | + - Create multiple color schemes (Light, Dark, High Contrast) | |
| 413 | + - Allow user customization | |
| 414 | + | |
| 415 | +4. **Text Rendering** | |
| 416 | + - Use Pango for text layout | |
| 417 | + - Smart label placement (only show when space permits) | |
| 418 | + - Ellipsize long filenames | |
| 419 | + - Show size labels with proper formatting (KB, MB, GB) | |
| 420 | + | |
| 421 | +5. **Performance** | |
| 422 | + - Implement viewport culling (only draw visible rectangles) | |
| 423 | + - Batch Cairo operations | |
| 424 | + - Profile draw time (target < 16ms / 60fps) | |
| 425 | + - Add FPS counter for debugging | |
| 426 | + | |
| 427 | +**Success Criteria:** | |
| 428 | +- Treemap looks beautiful with cushion effect | |
| 429 | +- Smooth scrolling and zooming | |
| 430 | +- Text is readable at all zoom levels | |
| 431 | +- Maintains 60fps on 10k visible nodes | |
| 432 | + | |
| 433 | +**Estimated Time:** 2 weeks | |
| 434 | + | |
| 435 | +--- | |
| 436 | + | |
| 437 | +### Phase 5: Interactivity (Weeks 9-10) | |
| 438 | + | |
| 439 | +**Goal:** Full SpaceSniffer-style navigation and interaction | |
| 440 | + | |
| 441 | +#### Tasks: | |
| 442 | +1. **Mouse Interaction** | |
| 443 | + - Implement hit testing (which rectangle was clicked?) | |
| 444 | + - Single-click: select and highlight | |
| 445 | + - Double-click: zoom into directory | |
| 446 | + - Right-click: show context menu | |
| 447 | + - Hover: show tooltip with details | |
| 448 | + | |
| 449 | +2. **Keyboard Navigation** | |
| 450 | + - Arrow keys: navigate siblings | |
| 451 | + - Enter: zoom into selected | |
| 452 | + - Backspace: zoom out to parent | |
| 453 | + - Delete: delete with confirmation (from sniffert) | |
| 454 | + - /: focus search box | |
| 455 | + - Escape: cancel/clear selection | |
| 456 | + | |
| 457 | +3. **Navigation UI** | |
| 458 | + - Breadcrumb bar at top | |
| 459 | + - Back/Forward buttons | |
| 460 | + - "Go up" button | |
| 461 | + - Animation on zoom transitions | |
| 462 | + | |
| 463 | +4. **Context Menus** | |
| 464 | + - Open in file manager | |
| 465 | + - Open file with default app | |
| 466 | + - Copy path to clipboard | |
| 467 | + - Delete with confirmation | |
| 468 | + - Properties dialog (size, modified date, permissions) | |
| 469 | + | |
| 470 | +5. **Selection State** | |
| 471 | + - Highlight selected rectangle | |
| 472 | + - Show details in status bar | |
| 473 | + - Persist selection across layout changes | |
| 474 | + - Visual feedback on hover | |
| 475 | + | |
| 476 | +**Success Criteria:** | |
| 477 | +- Navigation feels smooth and intuitive | |
| 478 | +- All keyboard shortcuts work | |
| 479 | +- Context menu matches macOS/Linux conventions | |
| 480 | +- No lag on interaction | |
| 481 | + | |
| 482 | +**Estimated Time:** 2 weeks | |
| 483 | + | |
| 484 | +--- | |
| 485 | + | |
| 486 | +### Phase 6: Search & Filter (Week 11) | |
| 487 | + | |
| 488 | +**Goal:** Find files quickly in large trees | |
| 489 | + | |
| 490 | +#### Tasks: | |
| 491 | +1. **Search UI** | |
| 492 | + - Add search entry in toolbar | |
| 493 | + - Show search results in tree view or list | |
| 494 | + - Highlight matching rectangles in treemap | |
| 495 | + - Clear search with Escape | |
| 496 | + | |
| 497 | +2. **Search Implementation** | |
| 498 | + - Full-text filename search | |
| 499 | + - Regex pattern support | |
| 500 | + - Case-insensitive option | |
| 501 | + - Search scope (current directory vs entire tree) | |
| 502 | + | |
| 503 | +3. **Filtering** | |
| 504 | + - Filter by file type (extensions) | |
| 505 | + - Filter by size range (e.g., files > 100MB) | |
| 506 | + - Filter by date range (modified in last 7 days) | |
| 507 | + - Combine multiple filters | |
| 508 | + | |
| 509 | +4. **Filter UI** | |
| 510 | + - Filter panel in sidebar (toggleable) | |
| 511 | + - Quick filters in toolbar (e.g., "Large files") | |
| 512 | + - Visual indication when filters active | |
| 513 | + - Reset filters button | |
| 514 | + | |
| 515 | +**Success Criteria:** | |
| 516 | +- Can find specific files in < 1 second | |
| 517 | +- Filters work on 100k+ files | |
| 518 | +- Search results update in real-time | |
| 519 | +- Filter state persists across zooms | |
| 520 | + | |
| 521 | +**Estimated Time:** 1 week | |
| 522 | + | |
| 523 | +--- | |
| 524 | + | |
| 525 | +### Phase 7: Polish & Configuration (Week 12) | |
| 526 | + | |
| 527 | +**Goal:** Production-ready with user customization | |
| 528 | + | |
| 529 | +#### Tasks: | |
| 530 | +1. **Settings Dialog** | |
| 531 | + - General: animation speed, default layout | |
| 532 | + - Colors: scheme selection, custom colors | |
| 533 | + - Scanning: skip patterns, depth limit | |
| 534 | + - Save settings to config file (~/.config/sniffly/config.ini) | |
| 535 | + | |
| 536 | +2. **Toolbar** | |
| 537 | + - Add all main actions | |
| 538 | + - Icon design (consistent with GTK) | |
| 539 | + - Tooltips for all buttons | |
| 540 | + - Responsive layout (collapse on small windows) | |
| 541 | + | |
| 542 | +3. **Status Bar** | |
| 543 | + - Show scan progress | |
| 544 | + - Show selected item details | |
| 545 | + - Show totals (files, directories, size) | |
| 546 | + - Show free space on drive | |
| 547 | + | |
| 548 | +4. **Error Handling** | |
| 549 | + - Graceful handling of permission denied | |
| 550 | + - User-friendly error messages | |
| 551 | + - Logging for debugging | |
| 552 | + - Crash recovery (save scan state) | |
| 553 | + | |
| 554 | +5. **Animations** | |
| 555 | + - Smooth zoom transitions | |
| 556 | + - Layout change animations | |
| 557 | + - Progress indicator animations | |
| 558 | + - Fade in/out for tooltips | |
| 559 | + | |
| 560 | +6. **Accessibility** | |
| 561 | + - Keyboard-only navigation support | |
| 562 | + - High-contrast mode | |
| 563 | + - Screen reader compatibility (GTK accessibility API) | |
| 564 | + - Configurable font sizes | |
| 565 | + | |
| 566 | +7. **Documentation** | |
| 567 | + - User manual (markdown) | |
| 568 | + - Keyboard shortcuts reference | |
| 569 | + - Build instructions | |
| 570 | + - Developer documentation | |
| 571 | + | |
| 572 | +**Success Criteria:** | |
| 573 | +- App feels polished and professional | |
| 574 | +- All settings persist correctly | |
| 575 | +- No crashes under normal usage | |
| 576 | +- Passes accessibility audit | |
| 577 | + | |
| 578 | +**Estimated Time:** 1 week | |
| 579 | + | |
| 580 | +--- | |
| 581 | + | |
| 582 | +### Phase 8: Testing & Packaging (Weeks 13-14) | |
| 583 | + | |
| 584 | +**Goal:** Release-ready packages for macOS and Linux | |
| 585 | + | |
| 586 | +#### Tasks: | |
| 587 | +1. **Testing** | |
| 588 | + - Unit tests for all modules | |
| 589 | + - Integration tests for GUI workflows | |
| 590 | + - Performance tests (1M files benchmark) | |
| 591 | + - Memory leak testing (valgrind) | |
| 592 | + - Test on multiple distributions | |
| 593 | + | |
| 594 | +2. **Packaging - macOS** | |
| 595 | + - Create .app bundle | |
| 596 | + - Sign with Apple Developer ID | |
| 597 | + - Create DMG installer | |
| 598 | + - Homebrew formula (homebrew-core PR) | |
| 599 | + - Test on macOS 12+ (Monterey, Ventura, Sonoma) | |
| 600 | + | |
| 601 | +3. **Packaging - Linux** | |
| 602 | + - Debian package (.deb) for Ubuntu/Debian | |
| 603 | + - RPM package (.rpm) for Fedora/RHEL | |
| 604 | + - AUR package for Arch | |
| 605 | + - Flatpak for universal Linux | |
| 606 | + - AppImage for portability | |
| 607 | + | |
| 608 | +4. **Distribution** | |
| 609 | + - Set up GitHub releases | |
| 610 | + - Create release notes | |
| 611 | + - Build CI/CD pipeline (GitHub Actions) | |
| 612 | + - Automated builds for each platform | |
| 613 | + | |
| 614 | +5. **Documentation** | |
| 615 | + - README with screenshots | |
| 616 | + - INSTALL guide for each platform | |
| 617 | + - CONTRIBUTING guide | |
| 618 | + - LICENSE (choose appropriate) | |
| 619 | + | |
| 620 | +6. **Marketing** | |
| 621 | + - Demo video showing features | |
| 622 | + - Comparison with SpaceSniffer | |
| 623 | + - Blog post: "Fortran Can Do GUIs!" | |
| 624 | + - Submit to package managers | |
| 625 | + | |
| 626 | +**Success Criteria:** | |
| 627 | +- Packages install cleanly on target platforms | |
| 628 | +- No runtime dependencies missing | |
| 629 | +- Passes package manager guidelines | |
| 630 | +- Downloads available from GitHub releases | |
| 631 | + | |
| 632 | +**Estimated Time:** 2 weeks | |
| 633 | + | |
| 634 | +--- | |
| 635 | + | |
| 636 | +## 🔧 Technical Challenges & Solutions | |
| 637 | + | |
| 638 | +### Challenge 1: gtk-fortran Learning Curve | |
| 639 | + | |
| 640 | +**Problem:** gtk-fortran is less documented than GTK's C API | |
| 641 | +**Solution:** | |
| 642 | +- Study gtk-fortran examples repo | |
| 643 | +- Reference GTK C documentation and translate | |
| 644 | +- Join gtk-fortran mailing list for support | |
| 645 | +- Contribute documentation back to project | |
| 646 | + | |
| 647 | +### Challenge 2: Real-time Layout Updates | |
| 648 | + | |
| 649 | +**Problem:** Layout recalculation can be slow for large trees | |
| 650 | +**Solution:** | |
| 651 | +- Incremental layout updates (only changed subtrees) | |
| 652 | +- Cache layout results aggressively | |
| 653 | +- Use dirty flagging to track what needs recalc | |
| 654 | +- Defer layout during rapid resizing (debounce) | |
| 655 | + | |
| 656 | +### Challenge 3: Memory Management | |
| 657 | + | |
| 658 | +**Problem:** Large directory trees consume significant memory | |
| 659 | +**Solution:** | |
| 660 | +- Use move_alloc to avoid deep copies (already in sniffert) | |
| 661 | +- Lazy loading for deep trees (only load visible levels) | |
| 662 | +- Configurable depth limit | |
| 663 | +- Manual deallocation of pruned subtrees | |
| 664 | + | |
| 665 | +### Challenge 4: Thread Safety with GTK | |
| 666 | + | |
| 667 | +**Problem:** GTK is not thread-safe; can't update UI from background thread | |
| 668 | +**Solution:** | |
| 669 | +- Use g_idle_add to queue UI updates from scan thread | |
| 670 | +- Wrap in gtk-fortran interface (c_funloc for callbacks) | |
| 671 | +- Keep shared state minimal and use mutex where needed | |
| 672 | +- Test thoroughly with ThreadSanitizer | |
| 673 | + | |
| 674 | +### Challenge 5: Cushion Shading Performance | |
| 675 | + | |
| 676 | +**Problem:** Gradient calculation for each rectangle can be slow | |
| 677 | +**Solution:** | |
| 678 | +- Pre-calculate cushion parameters during layout | |
| 679 | +- Use Cairo pattern caching | |
| 680 | +- Simplify lighting model (2-stop gradients) | |
| 681 | +- Profile and optimize hot paths | |
| 682 | + | |
| 683 | +### Challenge 6: Cross-Platform Differences | |
| 684 | + | |
| 685 | +**Problem:** macOS and Linux have different GTK behaviors | |
| 686 | +**Solution:** | |
| 687 | +- Test on both platforms regularly (CI/CD) | |
| 688 | +- Use GTK4's native backends (Cocoa on macOS) | |
| 689 | +- Avoid platform-specific code where possible | |
| 690 | +- Document known differences in README | |
| 691 | + | |
| 692 | +--- | |
| 693 | + | |
| 694 | +## 📊 Performance Targets | |
| 695 | + | |
| 696 | +### Scanning Performance | |
| 697 | + | |
| 698 | +| Metric | Target | Stretch Goal | | |
| 699 | +|--------|--------|--------------| | |
| 700 | +| Files per second | 10,000 | 50,000 | | |
| 701 | +| Memory per file | < 500 bytes | < 300 bytes | | |
| 702 | +| Initial scan (100k files) | < 10 seconds | < 5 seconds | | |
| 703 | +| Background scan overhead | < 5% CPU | < 2% CPU | | |
| 704 | + | |
| 705 | +### Rendering Performance | |
| 706 | + | |
| 707 | +| Metric | Target | Stretch Goal | | |
| 708 | +|--------|--------|--------------| | |
| 709 | +| Frame rate (1k visible nodes) | 60 fps | 120 fps | | |
| 710 | +| Frame rate (10k visible nodes) | 30 fps | 60 fps | | |
| 711 | +| Layout calculation (10k nodes) | < 100ms | < 50ms | | |
| 712 | +| Zoom animation duration | 300ms | 200ms | | |
| 713 | + | |
| 714 | +### Memory Usage | |
| 715 | + | |
| 716 | +| Metric | Target | Stretch Goal | | |
| 717 | +|--------|--------|--------------| | |
| 718 | +| Base application memory | < 50 MB | < 30 MB | | |
| 719 | +| Per-file overhead | < 500 bytes | < 300 bytes | | |
| 720 | +| Layout cache overhead | < 2x file data | < 1.5x file data | | |
| 721 | +| Total for 1M files | < 1 GB | < 500 MB | | |
| 722 | + | |
| 723 | +--- | |
| 724 | + | |
| 725 | +## 🎨 Visual Design | |
| 726 | + | |
| 727 | +### Color Schemes | |
| 728 | + | |
| 729 | +**Light Theme** (default on macOS) | |
| 730 | +- Background: #FFFFFF | |
| 731 | +- Directories: Shades of blue (#E3F2FD → #1976D2) | |
| 732 | +- Files: Shades of green (#E8F5E9 → #388E3C) | |
| 733 | +- Selected: Orange highlight (#FF6F00) | |
| 734 | + | |
| 735 | +**Dark Theme** (default on Linux) | |
| 736 | +- Background: #1E1E1E | |
| 737 | +- Directories: Shades of purple (#4A148C → #BA68C8) | |
| 738 | +- Files: Shades of teal (#004D40 → #26A69A) | |
| 739 | +- Selected: Yellow highlight (#FDD835) | |
| 740 | + | |
| 741 | +**High Contrast Theme** | |
| 742 | +- Strong borders on all rectangles | |
| 743 | +- High saturation colors | |
| 744 | +- Larger fonts | |
| 745 | +- No subtle gradients | |
| 746 | + | |
| 747 | +### File Type Colors | |
| 748 | + | |
| 749 | +| Type | Extension Examples | Color | | |
| 750 | +|------|-------------------|-------| | |
| 751 | +| Directories | (N/A) | Blue family | | |
| 752 | +| Documents | .pdf, .doc, .txt | Green family | | |
| 753 | +| Images | .jpg, .png, .gif | Orange family | | |
| 754 | +| Videos | .mp4, .mkv, .avi | Red family | | |
| 755 | +| Audio | .mp3, .flac, .wav | Purple family | | |
| 756 | +| Code | .c, .py, .js, .f90 | Cyan family | | |
| 757 | +| Archives | .zip, .tar, .gz | Brown family | | |
| 758 | +| Executables | .exe, .app, .bin | Gray family | | |
| 759 | + | |
| 760 | +### Typography | |
| 761 | + | |
| 762 | +- **Main Labels:** System font (San Francisco on macOS, Ubuntu on Linux) | |
| 763 | +- **Size Labels:** Monospace font for alignment | |
| 764 | +- **Minimum Readable Size:** 10pt | |
| 765 | +- **Label Visibility:** Only show when rectangle width > 60px and height > 20px | |
| 766 | + | |
| 767 | +--- | |
| 768 | + | |
| 769 | +## 🚀 Distribution Strategy | |
| 770 | + | |
| 771 | +### Package Managers | |
| 772 | + | |
| 773 | +**macOS:** | |
| 774 | +1. **Homebrew** (primary) | |
| 775 | + ```bash | |
| 776 | + brew install sniffly | |
| 777 | + ``` | |
| 778 | + - Submit formula to homebrew-core | |
| 779 | + - Automatic updates | |
| 780 | + - High visibility | |
| 781 | + | |
| 782 | +2. **MacPorts** (secondary) | |
| 783 | + ```bash | |
| 784 | + sudo port install sniffly | |
| 785 | + ``` | |
| 786 | + | |
| 787 | +**Linux:** | |
| 788 | +1. **Debian/Ubuntu** (APT) | |
| 789 | + ```bash | |
| 790 | + sudo apt install sniffly | |
| 791 | + ``` | |
| 792 | + - Upload to Debian repositories | |
| 793 | + - Backport to older releases | |
| 794 | + | |
| 795 | +2. **Arch** (AUR) | |
| 796 | + ```bash | |
| 797 | + yay -S sniffly | |
| 798 | + ``` | |
| 799 | + - Maintain PKGBUILD in AUR | |
| 800 | + - Community-maintained | |
| 801 | + | |
| 802 | +3. **Fedora/RHEL** (DNF) | |
| 803 | + ```bash | |
| 804 | + sudo dnf install sniffly | |
| 805 | + ``` | |
| 806 | + - Submit to Fedora repositories | |
| 807 | + | |
| 808 | +4. **Universal** (Flatpak) | |
| 809 | + ```bash | |
| 810 | + flatpak install sniffly | |
| 811 | + ``` | |
| 812 | + - Works on all distributions | |
| 813 | + - Sandboxed environment | |
| 814 | + | |
| 815 | +### GitHub Releases | |
| 816 | + | |
| 817 | +- Provide pre-built binaries for: | |
| 818 | + - macOS (arm64, x86_64) | |
| 819 | + - Linux (x86_64, arm64) | |
| 820 | +- Include source tarballs | |
| 821 | +- Automated release via GitHub Actions | |
| 822 | +- Semantic versioning (v1.0.0, v1.1.0, etc.) | |
| 823 | + | |
| 824 | +--- | |
| 825 | + | |
| 826 | +## 📚 Dependencies | |
| 827 | + | |
| 828 | +### Build Dependencies | |
| 829 | + | |
| 830 | +| Dependency | Version | Package Name (Homebrew) | Package Name (APT) | | |
| 831 | +|------------|---------|-------------------------|-------------------| | |
| 832 | +| gfortran | 9.0+ | gcc | gfortran | | |
| 833 | +| GTK4 | 4.0+ | gtk4 | libgtk-4-dev | | |
| 834 | +| gtk-fortran | 3.24.41+ | (build from source) | (build from source) | | |
| 835 | +| GLib | 2.80+ | glib | libglib2.0-dev | | |
| 836 | +| Cairo | 1.18+ | cairo | libcairo2-dev | | |
| 837 | +| Pango | 1.50+ | pango | libpango1.0-dev | | |
| 838 | +| Meson | 0.60+ | meson | meson | | |
| 839 | +| Ninja | 1.10+ | ninja | ninja-build | | |
| 840 | + | |
| 841 | +### Runtime Dependencies | |
| 842 | + | |
| 843 | +| Dependency | Version | Included In | | |
| 844 | +|------------|---------|-------------| | |
| 845 | +| GTK4 runtime | 4.0+ | Package | | |
| 846 | +| GLib runtime | 2.80+ | Package | | |
| 847 | +| Cairo runtime | 1.18+ | Package | | |
| 848 | + | |
| 849 | +--- | |
| 850 | + | |
| 851 | +## 🧪 Testing Strategy | |
| 852 | + | |
| 853 | +### Unit Tests | |
| 854 | + | |
| 855 | +**Location:** `test/` directory | |
| 856 | +**Framework:** FUnit or simple assertion framework | |
| 857 | +**Coverage:** > 80% of non-GUI code | |
| 858 | + | |
| 859 | +**Test Categories:** | |
| 860 | +1. Layout algorithms (verify aspect ratios, area conservation) | |
| 861 | +2. File scanning (mock filesystem) | |
| 862 | +3. State management (selection, navigation) | |
| 863 | +4. Color calculations (verify gradients) | |
| 864 | +5. String formatting (size display) | |
| 865 | + | |
| 866 | +### Integration Tests | |
| 867 | + | |
| 868 | +**Framework:** Python + GTK inspector + screenshot comparison | |
| 869 | + | |
| 870 | +**Test Scenarios:** | |
| 871 | +1. Launch app and scan /tmp | |
| 872 | +2. Navigate into directory via click | |
| 873 | +3. Zoom out via breadcrumb | |
| 874 | +4. Switch layout algorithm | |
| 875 | +5. Apply filters | |
| 876 | +6. Delete file with confirmation | |
| 877 | +7. Search for file | |
| 878 | +8. Change color scheme | |
| 879 | + | |
| 880 | +### Performance Tests | |
| 881 | + | |
| 882 | +**Benchmarks:** | |
| 883 | +1. Scan 1M files (measure time, memory) | |
| 884 | +2. Layout calculation (measure time for various node counts) | |
| 885 | +3. Rendering (measure FPS for various node counts) | |
| 886 | +4. Memory growth over time (detect leaks) | |
| 887 | + | |
| 888 | +**Tools:** | |
| 889 | +- gprof for profiling | |
| 890 | +- valgrind for memory leaks | |
| 891 | +- perf for Linux profiling | |
| 892 | +- Instruments for macOS profiling | |
| 893 | + | |
| 894 | +### Manual Testing | |
| 895 | + | |
| 896 | +**Checklist:** | |
| 897 | +- [ ] Install on clean macOS system | |
| 898 | +- [ ] Install on clean Ubuntu system | |
| 899 | +- [ ] Install on clean Arch system | |
| 900 | +- [ ] Scan various directories (/usr, /home, /Applications) | |
| 901 | +- [ ] Resize window aggressively | |
| 902 | +- [ ] Navigate deep directory trees | |
| 903 | +- [ ] Delete files and verify | |
| 904 | +- [ ] Close and reopen (verify settings persist) | |
| 905 | +- [ ] Run for 1 hour (verify stability) | |
| 906 | + | |
| 907 | +--- | |
| 908 | + | |
| 909 | +## 🔒 Security Considerations | |
| 910 | + | |
| 911 | +### File System Access | |
| 912 | + | |
| 913 | +- **Principle:** Only access what user explicitly scans | |
| 914 | +- **Implementation:** | |
| 915 | + - No background scanning without user action | |
| 916 | + - Respect system permissions | |
| 917 | + - Skip symlinks to avoid cycles | |
| 918 | + - Warn on permission denied | |
| 919 | + | |
| 920 | +### Deletion Safety | |
| 921 | + | |
| 922 | +- **Principle:** Make it hard to delete by accident | |
| 923 | +- **Implementation:** | |
| 924 | + - Always show confirmation dialog | |
| 925 | + - Show full path and size | |
| 926 | + - For large deletions (>1GB), require typing name | |
| 927 | + - No "remember my choice" option | |
| 928 | + - Log deletions for audit trail | |
| 929 | + | |
| 930 | +### Sandboxing | |
| 931 | + | |
| 932 | +- **Flatpak:** Request filesystem access permission | |
| 933 | +- **macOS:** Request Full Disk Access if needed | |
| 934 | +- **General:** Run with user privileges (never root) | |
| 935 | + | |
| 936 | +--- | |
| 937 | + | |
| 938 | +## 📈 Success Metrics | |
| 939 | + | |
| 940 | +### Development Success | |
| 941 | + | |
| 942 | +- [ ] All phases completed on schedule | |
| 943 | +- [ ] No P0 bugs at launch | |
| 944 | +- [ ] Performance targets met | |
| 945 | +- [ ] Passes all tests on both platforms | |
| 946 | + | |
| 947 | +### Adoption Success | |
| 948 | + | |
| 949 | +- [ ] 1,000 stars on GitHub in first 3 months | |
| 950 | +- [ ] 10,000 downloads in first 6 months | |
| 951 | +- [ ] Accepted into Homebrew and apt repositories | |
| 952 | +- [ ] Featured on Hacker News or /r/programming | |
| 953 | + | |
| 954 | +### Community Success | |
| 955 | + | |
| 956 | +- [ ] 5+ external contributors | |
| 957 | +- [ ] 10+ issues/PRs from community | |
| 958 | +- [ ] Mentioned in "Fortran can do that?" articles | |
| 959 | +- [ ] Used as gtk-fortran showcase example | |
| 960 | + | |
| 961 | +--- | |
| 962 | + | |
| 963 | +## 🎓 Learning Resources | |
| 964 | + | |
| 965 | +### gtk-fortran | |
| 966 | + | |
| 967 | +- Official Wiki: https://github.com/vmagnin/gtk-fortran/wiki | |
| 968 | +- Examples: https://github.com/vmagnin/gtk-fortran/tree/master/examples | |
| 969 | +- Mailing List: gtk-fortran-devel@lists.sourceforge.net | |
| 970 | + | |
| 971 | +### GTK4 | |
| 972 | + | |
| 973 | +- API Reference: https://docs.gtk.org/gtk4/ | |
| 974 | +- Tutorial: https://www.gtk.org/docs/getting-started/ | |
| 975 | +- Migration Guide (GTK3→4): https://docs.gtk.org/gtk4/migrating-3to4.html | |
| 976 | + | |
| 977 | +### Cairo | |
| 978 | + | |
| 979 | +- API Reference: https://www.cairographics.org/manual/ | |
| 980 | +- Tutorial: https://www.cairographics.org/tutorial/ | |
| 981 | +- Samples: https://www.cairographics.org/samples/ | |
| 982 | + | |
| 983 | +### Treemap Algorithms | |
| 984 | + | |
| 985 | +- Squarified Treemaps: https://www.win.tue.nl/~vanwijk/stm.pdf | |
| 986 | +- Cushion Treemaps: https://www.win.tue.nl/~vanwijk/ctm.pdf | |
| 987 | +- Treemap History: http://www.cs.umd.edu/hcil/treemap-history/ | |
| 988 | + | |
| 989 | +--- | |
| 990 | + | |
| 991 | +## 🗺️ Roadmap | |
| 992 | + | |
| 993 | +### Version 1.0 (12 weeks) | |
| 994 | + | |
| 995 | +Core features: | |
| 996 | +- Real-time scanning with progress | |
| 997 | +- Squarified and cushioned layouts | |
| 998 | +- Interactive navigation (click, zoom, breadcrumb) | |
| 999 | +- Search and filter | |
| 1000 | +- Delete with confirmation | |
| 1001 | +- Settings persistence | |
| 1002 | +- Package for macOS and Linux | |
| 1003 | + | |
| 1004 | +### Version 1.1 (Post-launch +1 month) | |
| 1005 | + | |
| 1006 | +Polish and community feedback: | |
| 1007 | +- Bug fixes from user reports | |
| 1008 | +- Performance improvements | |
| 1009 | +- Additional color schemes | |
| 1010 | +- Export treemap as image | |
| 1011 | +- Localization support (i18n) | |
| 1012 | + | |
| 1013 | +### Version 1.2 (Post-launch +3 months) | |
| 1014 | + | |
| 1015 | +Advanced features: | |
| 1016 | +- Compare two directory snapshots | |
| 1017 | +- Show file age heatmap | |
| 1018 | +- Bookmarks for frequent directories | |
| 1019 | +- Multiple tabs | |
| 1020 | +- Plugins/extensions system | |
| 1021 | + | |
| 1022 | +### Version 2.0 (Post-launch +6 months) | |
| 1023 | + | |
| 1024 | +Major enhancements: | |
| 1025 | +- Network drive support | |
| 1026 | +- Cloud storage integration (investigate) | |
| 1027 | +- Advanced filters (by owner, permissions) | |
| 1028 | +- Duplicate file detection | |
| 1029 | +- Custom layout algorithms | |
| 1030 | + | |
| 1031 | +--- | |
| 1032 | + | |
| 1033 | +## 🤝 Contributing | |
| 1034 | + | |
| 1035 | +### For Fortran Developers | |
| 1036 | + | |
| 1037 | +This project is a showcase of Fortran's capabilities! Contributions welcome: | |
| 1038 | +- Algorithm optimizations | |
| 1039 | +- New layout algorithms | |
| 1040 | +- Performance improvements | |
| 1041 | +- Code reviews | |
| 1042 | + | |
| 1043 | +### For GUI Developers | |
| 1044 | + | |
| 1045 | +Help make sniffly beautiful: | |
| 1046 | +- UI/UX improvements | |
| 1047 | +- Icon design | |
| 1048 | +- Color scheme contributions | |
| 1049 | +- Accessibility enhancements | |
| 1050 | + | |
| 1051 | +### For Package Maintainers | |
| 1052 | + | |
| 1053 | +Help distribute sniffly: | |
| 1054 | +- Create packages for your favorite distro | |
| 1055 | +- Test on various platforms | |
| 1056 | +- Report packaging issues | |
| 1057 | +- Maintain downstream packages | |
| 1058 | + | |
| 1059 | +--- | |
| 1060 | + | |
| 1061 | +## 📄 License | |
| 1062 | + | |
| 1063 | +**Recommendation:** MIT or BSD-3-Clause | |
| 1064 | + | |
| 1065 | +**Rationale:** | |
| 1066 | +- Permissive enough for wide adoption | |
| 1067 | +- Compatible with GTK's LGPL | |
| 1068 | +- Allows commercial use | |
| 1069 | +- Simple and well-understood | |
| 1070 | + | |
| 1071 | +--- | |
| 1072 | + | |
| 1073 | +## 🎉 Conclusion | |
| 1074 | + | |
| 1075 | +This project will prove that Fortran can create modern, beautiful GUI applications that rival commercial software. By combining gtk-fortran's solid bindings with your already-excellent treemap algorithms, sniffly will be a showcase of: | |
| 1076 | + | |
| 1077 | +1. **Fortran's Versatility:** Not just for scientific computing! | |
| 1078 | +2. **Open Source Quality:** Unix users deserve great tools | |
| 1079 | +3. **Performance:** Native code, no interpreter overhead | |
| 1080 | +4. **Cross-Platform:** One codebase, multiple platforms | |
| 1081 | + | |
| 1082 | +**Let's build this!** 🚀 | |
| 1083 | + | |
| 1084 | +--- | |
| 1085 | + | |
| 1086 | +**Next Steps:** | |
| 1087 | +1. Install GTK4 and gtk-fortran | |
| 1088 | +2. Create minimal GTK window test | |
| 1089 | +3. Port core scanning logic | |
| 1090 | +4. Start Phase 1 implementation | |
| 1091 | + | |
| 1092 | +**Questions? Need help?** | |
| 1093 | +- gtk-fortran community is friendly and helpful | |
| 1094 | +- GTK documentation is extensive | |
| 1095 | +- This plan is a living document - iterate as needed! | |
| 1096 | + | |
| 1097 | +--- | |
| 1098 | + | |
| 1099 | +**Document Version:** 1.0 | |
| 1100 | +**Last Updated:** 2025-11-04 | |
| 1101 | +**Status:** Ready to Begin Development | |
SPACESNIFFER_FEATURES.mdadded@@ -0,0 +1,135 @@ | ||
| 1 | +# SpaceSniffer Feature Parity Checklist | |
| 2 | + | |
| 3 | +## 🎯 Goal: Near 1:1 Clone of SpaceSniffer for Unix | |
| 4 | + | |
| 5 | +### ✅ Completed (Phase 1-2) | |
| 6 | +- [x] GTK4 window with Cairo rendering | |
| 7 | +- [x] Real directory scanning | |
| 8 | +- [x] Squarified treemap layout algorithm | |
| 9 | +- [x] Basic color coding | |
| 10 | +- [x] Handles large directories (32GB tested) | |
| 11 | +- [x] Window resize/redraw | |
| 12 | + | |
| 13 | +### 🚧 In Progress (Phase 3-4) | |
| 14 | +- [x] **Directory Chooser** - Command-line argument support (GUI dialog deferred) | |
| 15 | +- [x] **File Names on Rectangles** - Show names when space permits | |
| 16 | +- [x] **Toolbar** - Scan and Quit buttons | |
| 17 | +- [x] **Status Bar** - Show ready status | |
| 18 | +- [x] **Top-Level Rendering** - Show only direct children (no deep nesting) | |
| 19 | +- [ ] **Size Labels** - Display MB/GB on large rectangles | |
| 20 | + | |
| 21 | +### 📋 Critical Features (Phase 5-6) | |
| 22 | +- [ ] **Click to Select** - Highlight selected rectangle | |
| 23 | +- [ ] **Double-click to Zoom** - Navigate into directories | |
| 24 | +- [ ] **Breadcrumb Bar** - Show current path, allow navigation back | |
| 25 | +- [ ] **Hover Tooltips** - Show full path and details on hover | |
| 26 | +- [ ] **Context Menu** - Right-click: Open, Delete, Properties | |
| 27 | +- [ ] **Keyboard Navigation** - Arrow keys, Enter, Backspace | |
| 28 | +- [ ] **Delete with Confirmation** - Safe deletion workflow | |
| 29 | + | |
| 30 | +### 🎨 Visual Enhancements (Phase 7) | |
| 31 | +- [ ] **Cushioned Treemap** - 3D shading effect (Van Wijk algorithm) | |
| 32 | +- [ ] **Better Color Schemes** - By file type, age, or extension | |
| 33 | +- [ ] **Smart Label Placement** - Only show when readable | |
| 34 | +- [ ] **Selection Highlight** - Clear visual feedback | |
| 35 | +- [ ] **Hover Effect** - Lighten/darken on mouse over | |
| 36 | +- [ ] **Smooth Animations** - Fade in/out, zoom transitions | |
| 37 | + | |
| 38 | +### 🔍 Advanced Features (Phase 8) | |
| 39 | +- [ ] **Search Bar** - Find files by name (regex) | |
| 40 | +- [ ] **Filter Panel** - By type, size, date | |
| 41 | +- [ ] **Real-time Scanning** - Animate growth as files are found | |
| 42 | +- [ ] **Pause/Resume Scan** - Control during scanning | |
| 43 | +- [ ] **Free Space Display** - Show available disk space | |
| 44 | +- [ ] **Multiple Color Modes** - Switch between schemes | |
| 45 | + | |
| 46 | +### 📊 Information Display | |
| 47 | +- [ ] **Status Bar Items:** | |
| 48 | + - Currently scanning: /path/to/dir | |
| 49 | + - Items scanned: 1,234 / 5,678 | |
| 50 | + - Total size: 32.5 GB | |
| 51 | + - Selected: filename.ext (1.2 MB) | |
| 52 | + - Free space: 128 GB | |
| 53 | + | |
| 54 | +### 🖱️ Mouse & Keyboard | |
| 55 | +- [ ] **Mouse:** | |
| 56 | + - Left click: Select | |
| 57 | + - Double-click: Zoom in | |
| 58 | + - Right-click: Context menu | |
| 59 | + - Hover: Show tooltip | |
| 60 | + | |
| 61 | +- [ ] **Keyboard:** | |
| 62 | + - Arrow keys: Navigate siblings | |
| 63 | + - Enter: Zoom into selected | |
| 64 | + - Backspace: Zoom out to parent | |
| 65 | + - Delete: Delete selected (with confirmation) | |
| 66 | + - Ctrl+F: Search | |
| 67 | + - Ctrl+Q: Quit | |
| 68 | + - /: Focus search | |
| 69 | + - Escape: Clear selection | |
| 70 | + | |
| 71 | +### 🎨 Visual Polish | |
| 72 | +- [ ] **Colors:** | |
| 73 | + - Directories: Blue family | |
| 74 | + - Documents: Green family | |
| 75 | + - Images: Orange family | |
| 76 | + - Videos: Red family | |
| 77 | + - Audio: Purple family | |
| 78 | + - Code: Cyan family | |
| 79 | + - Archives: Brown family | |
| 80 | + | |
| 81 | +- [ ] **Fonts:** | |
| 82 | + - Names: System font, bold for directories | |
| 83 | + - Sizes: Monospace for alignment | |
| 84 | + - Min size: 10pt readable | |
| 85 | + | |
| 86 | +### 🔧 Settings/Preferences | |
| 87 | +- [ ] **Configurable:** | |
| 88 | + - Layout algorithm (Squarified vs Cushioned) | |
| 89 | + - Color scheme | |
| 90 | + - Animation speed | |
| 91 | + - Font size | |
| 92 | + - Skip directories (.git, node_modules, etc.) | |
| 93 | + - Scan depth limit | |
| 94 | + | |
| 95 | +--- | |
| 96 | + | |
| 97 | +## 📈 Current Progress: ~45% Complete | |
| 98 | + | |
| 99 | +### What Works Now: | |
| 100 | +✅ Scanning (4GB+ tested, scans entire tree) | |
| 101 | +✅ Layout calculation (squarified treemap algorithm) | |
| 102 | +✅ Smart rendering (shows only top-level, not nested) | |
| 103 | +✅ Window resizing with proper layout recalculation | |
| 104 | +✅ Directory selection (via command-line argument) | |
| 105 | +✅ File/directory names on rectangles (when space permits) | |
| 106 | +✅ Toolbar with Scan and Quit buttons | |
| 107 | +✅ Status bar showing scan status | |
| 108 | + | |
| 109 | +### Immediate Priorities (Next 3 Commits): | |
| 110 | +1. **Click-to-select** - Highlight selected rectangle | |
| 111 | +2. **Double-click navigation** - Zoom into directories | |
| 112 | +3. **Breadcrumbs** - Show current path and navigate back | |
| 113 | + | |
| 114 | +### Next Week Goals: | |
| 115 | +4. Click selection | |
| 116 | +5. Zoom navigation | |
| 117 | +6. Breadcrumbs | |
| 118 | +7. Tooltips | |
| 119 | + | |
| 120 | +--- | |
| 121 | + | |
| 122 | +## 🎯 Success Criteria | |
| 123 | + | |
| 124 | +A successful SpaceSniffer clone must: | |
| 125 | +- ✅ Scan and visualize any directory | |
| 126 | +- ⏳ Show file names and sizes clearly | |
| 127 | +- ⏳ Allow navigation (click, zoom, back) | |
| 128 | +- ⏳ Provide deletion with safety | |
| 129 | +- ⏳ Look professional (toolbar, status bar) | |
| 130 | +- ⏳ Handle large directories (millions of files) | |
| 131 | +- ⏳ Be as fast or faster than SpaceSniffer | |
| 132 | + | |
| 133 | +--- | |
| 134 | + | |
| 135 | +**We're making great progress, but we're just getting started!** 🚀 | |
Sniffly.app/Contents/Info.plistadded@@ -0,0 +1,32 @@ | ||
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| 3 | +<plist version="1.0"> | |
| 4 | +<dict> | |
| 5 | + <key>CFBundleExecutable</key> | |
| 6 | + <string>sniffly</string> | |
| 7 | + <key>CFBundleIdentifier</key> | |
| 8 | + <string>org.fortrangoingonforty.sniffly</string> | |
| 9 | + <key>CFBundleName</key> | |
| 10 | + <string>Sniffly</string> | |
| 11 | + <key>CFBundleDisplayName</key> | |
| 12 | + <string>Sniffly</string> | |
| 13 | + <key>CFBundleVersion</key> | |
| 14 | + <string>0.2.7</string> | |
| 15 | + <key>CFBundleShortVersionString</key> | |
| 16 | + <string>0.2.7</string> | |
| 17 | + <key>CFBundlePackageType</key> | |
| 18 | + <string>APPL</string> | |
| 19 | + <key>CFBundleSignature</key> | |
| 20 | + <string>SNIF</string> | |
| 21 | + <key>CFBundleIconFile</key> | |
| 22 | + <string>sniffly</string> | |
| 23 | + <key>LSMinimumSystemVersion</key> | |
| 24 | + <string>11.0</string> | |
| 25 | + <key>NSHighResolutionCapable</key> | |
| 26 | + <true/> | |
| 27 | + <key>NSRequiresAquaSystemAppearance</key> | |
| 28 | + <false/> | |
| 29 | + <key>LSApplicationCategoryType</key> | |
| 30 | + <string>public.app-category.utilities</string> | |
| 31 | +</dict> | |
| 32 | +</plist> | |
Sniffly.app/Contents/MacOS/sniffly-binaddedSniffly.app/Contents/PkgInfoadded@@ -0,0 +1,1 @@ | ||
| 1 | +APPLSNIF | |
Sniffly.app/Contents/_CodeSignature/CodeDirectoryaddedSniffly.app/Contents/_CodeSignature/CodeRequirementsaddedSniffly.app/Contents/_CodeSignature/CodeRequirements-1addedSniffly.app/Contents/_CodeSignature/CodeResourcesadded@@ -0,0 +1,999 @@ | ||
| 1 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| 3 | +<plist version="1.0"> | |
| 4 | +<dict> | |
| 5 | + <key>files</key> | |
| 6 | + <dict> | |
| 7 | + <key>Resources/share/glib-2.0/schemas/gschema.dtd</key> | |
| 8 | + <data> | |
| 9 | + BY2rQNdph/3FfTHf7nnlVZ+U2/0= | |
| 10 | + </data> | |
| 11 | + <key>Resources/share/glib-2.0/schemas/gschemas.compiled</key> | |
| 12 | + <data> | |
| 13 | + PUbZavbyH7VUpsQKezAGbofeWEo= | |
| 14 | + </data> | |
| 15 | + <key>Resources/share/glib-2.0/schemas/org.gtk.Demo4.gschema.xml</key> | |
| 16 | + <data> | |
| 17 | + iFjAPaAxzf6zp4IKKXUsaS2Wi8g= | |
| 18 | + </data> | |
| 19 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Inspector.gschema.xml</key> | |
| 20 | + <data> | |
| 21 | + jgzjvDTQrcwV4hrD/HgQ+N4AFhM= | |
| 22 | + </data> | |
| 23 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.ColorChooser.gschema.xml</key> | |
| 24 | + <data> | |
| 25 | + /iNWMcJOzPva8L3ulwQ6HwtRgXM= | |
| 26 | + </data> | |
| 27 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.Debug.gschema.xml</key> | |
| 28 | + <data> | |
| 29 | + qL7MxCdaSI3mj4iLFTPn1m7nrrw= | |
| 30 | + </data> | |
| 31 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.EmojiChooser.gschema.xml</key> | |
| 32 | + <data> | |
| 33 | + PJHFmo5OoI3zUvKllylBp0gALEE= | |
| 34 | + </data> | |
| 35 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.FileChooser.gschema.xml</key> | |
| 36 | + <data> | |
| 37 | + U9fKvSUl9DJ/cEdecFSvzRxA16M= | |
| 38 | + </data> | |
| 39 | + <key>Resources/share/gtk-4.0/emoji/bn.gresource</key> | |
| 40 | + <data> | |
| 41 | + Tt0UUyhxmAqGPDQhIghAnO1//cE= | |
| 42 | + </data> | |
| 43 | + <key>Resources/share/gtk-4.0/emoji/da.gresource</key> | |
| 44 | + <data> | |
| 45 | + qofpYMTzCx5rurhSCQ49Eq/gQd4= | |
| 46 | + </data> | |
| 47 | + <key>Resources/share/gtk-4.0/emoji/de.gresource</key> | |
| 48 | + <data> | |
| 49 | + UiZSMGYJhJI45AzwpH2uNu0I19M= | |
| 50 | + </data> | |
| 51 | + <key>Resources/share/gtk-4.0/emoji/es.gresource</key> | |
| 52 | + <data> | |
| 53 | + b4UyvR+WYMy2I1vuC9+0cH46cj8= | |
| 54 | + </data> | |
| 55 | + <key>Resources/share/gtk-4.0/emoji/et.gresource</key> | |
| 56 | + <data> | |
| 57 | + bhg3hLjbjGntSbc6hpUGyIJBIxc= | |
| 58 | + </data> | |
| 59 | + <key>Resources/share/gtk-4.0/emoji/fi.gresource</key> | |
| 60 | + <data> | |
| 61 | + jlJsJG7RP0tZypyLFafqVgSUsUc= | |
| 62 | + </data> | |
| 63 | + <key>Resources/share/gtk-4.0/emoji/fr.gresource</key> | |
| 64 | + <data> | |
| 65 | + eTI1ovErYI/kFxgga0MlceFwxAY= | |
| 66 | + </data> | |
| 67 | + <key>Resources/share/gtk-4.0/emoji/hi.gresource</key> | |
| 68 | + <data> | |
| 69 | + I3Ur/v5aarS3QPeCQkOPobCq8t4= | |
| 70 | + </data> | |
| 71 | + <key>Resources/share/gtk-4.0/emoji/hu.gresource</key> | |
| 72 | + <data> | |
| 73 | + xlwpk+cy3V7DReSYDiXPb7WIatk= | |
| 74 | + </data> | |
| 75 | + <key>Resources/share/gtk-4.0/emoji/it.gresource</key> | |
| 76 | + <data> | |
| 77 | + nwlNj5cx3HGmPLlEIoZch9538ZQ= | |
| 78 | + </data> | |
| 79 | + <key>Resources/share/gtk-4.0/emoji/ja.gresource</key> | |
| 80 | + <data> | |
| 81 | + cvOWCi+1srwn1yX8CAuDqO4wI0o= | |
| 82 | + </data> | |
| 83 | + <key>Resources/share/gtk-4.0/emoji/ko.gresource</key> | |
| 84 | + <data> | |
| 85 | + bKjuMyXqmqLWm+A6RQCmmoJe3jA= | |
| 86 | + </data> | |
| 87 | + <key>Resources/share/gtk-4.0/emoji/lt.gresource</key> | |
| 88 | + <data> | |
| 89 | + LMQx/lMs3iwIKzc1w4CfuueNjEA= | |
| 90 | + </data> | |
| 91 | + <key>Resources/share/gtk-4.0/emoji/ms.gresource</key> | |
| 92 | + <data> | |
| 93 | + X3/AN4eWGYG/dq4w8SExYqTwG70= | |
| 94 | + </data> | |
| 95 | + <key>Resources/share/gtk-4.0/emoji/nb.gresource</key> | |
| 96 | + <data> | |
| 97 | + FyeaCXibNgtT4H2/v94AqcN4P9Q= | |
| 98 | + </data> | |
| 99 | + <key>Resources/share/gtk-4.0/emoji/nl.gresource</key> | |
| 100 | + <data> | |
| 101 | + 1JRe4GqcXqPIz0elmFY6sgvKcxU= | |
| 102 | + </data> | |
| 103 | + <key>Resources/share/gtk-4.0/emoji/pl.gresource</key> | |
| 104 | + <data> | |
| 105 | + pUi/VXwKnYDW6vW1XFDYpbcDPM8= | |
| 106 | + </data> | |
| 107 | + <key>Resources/share/gtk-4.0/emoji/pt.gresource</key> | |
| 108 | + <data> | |
| 109 | + ++WVXFGgVBcz+mw2tAW3jlQnRTI= | |
| 110 | + </data> | |
| 111 | + <key>Resources/share/gtk-4.0/emoji/ru.gresource</key> | |
| 112 | + <data> | |
| 113 | + 20h8jXi2Xl/CpqDgSDFvFW4tgW8= | |
| 114 | + </data> | |
| 115 | + <key>Resources/share/gtk-4.0/emoji/sv.gresource</key> | |
| 116 | + <data> | |
| 117 | + ZIgYlHeEl4zkH9DZ0tTFAV7fZCY= | |
| 118 | + </data> | |
| 119 | + <key>Resources/share/gtk-4.0/emoji/th.gresource</key> | |
| 120 | + <data> | |
| 121 | + XOao2Fz+KGA62HJPY1Zz6TrQQPg= | |
| 122 | + </data> | |
| 123 | + <key>Resources/share/gtk-4.0/emoji/uk.gresource</key> | |
| 124 | + <data> | |
| 125 | + f5mTp7g47Tu5E6uxfb/Hje+aoYE= | |
| 126 | + </data> | |
| 127 | + <key>Resources/share/gtk-4.0/emoji/zh.gresource</key> | |
| 128 | + <data> | |
| 129 | + acEBrz5jBFRfrrlMYH7TY2Dt5CY= | |
| 130 | + </data> | |
| 131 | + <key>Resources/share/gtk-4.0/gtk4builder.rng</key> | |
| 132 | + <data> | |
| 133 | + X93vrJ9nzUWc1PDsKhmRjfy5Q+U= | |
| 134 | + </data> | |
| 135 | + <key>Resources/share/gtk-4.0/valgrind/gtk.supp</key> | |
| 136 | + <data> | |
| 137 | + 6B5NUN/9YTSLoE1eHra2FDmNNGE= | |
| 138 | + </data> | |
| 139 | + </dict> | |
| 140 | + <key>files2</key> | |
| 141 | + <dict> | |
| 142 | + <key>Frameworks/libX11.6.dylib</key> | |
| 143 | + <dict> | |
| 144 | + <key>cdhash</key> | |
| 145 | + <data> | |
| 146 | + LDk33XspR4u404J+T7DLNfTb3eY= | |
| 147 | + </data> | |
| 148 | + <key>requirement</key> | |
| 149 | + <string>cdhash H"2c3937dd7b29478bb8d3827e4fb0cb35f4dbdde6"</string> | |
| 150 | + </dict> | |
| 151 | + <key>Frameworks/libXau.6.dylib</key> | |
| 152 | + <dict> | |
| 153 | + <key>cdhash</key> | |
| 154 | + <data> | |
| 155 | + cjIEIGitnJwlg3b708q/xO4smu8= | |
| 156 | + </data> | |
| 157 | + <key>requirement</key> | |
| 158 | + <string>cdhash H"7232042068ad9c9c258376fbd3cabfc4ee2c9aef"</string> | |
| 159 | + </dict> | |
| 160 | + <key>Frameworks/libXdmcp.6.dylib</key> | |
| 161 | + <dict> | |
| 162 | + <key>cdhash</key> | |
| 163 | + <data> | |
| 164 | + fC6ZM/p6a/1v9lnWViyake9hXCI= | |
| 165 | + </data> | |
| 166 | + <key>requirement</key> | |
| 167 | + <string>cdhash H"7c2e9933fa7a6bfd6ff659d6562c9a91ef615c22"</string> | |
| 168 | + </dict> | |
| 169 | + <key>Frameworks/libXext.6.dylib</key> | |
| 170 | + <dict> | |
| 171 | + <key>cdhash</key> | |
| 172 | + <data> | |
| 173 | + s+K/hMmpwnzaid73d2mDsSawgTA= | |
| 174 | + </data> | |
| 175 | + <key>requirement</key> | |
| 176 | + <string>cdhash H"b3e2bf84c9a9c27cda89def7776983b126b08130"</string> | |
| 177 | + </dict> | |
| 178 | + <key>Frameworks/libXrender.1.dylib</key> | |
| 179 | + <dict> | |
| 180 | + <key>cdhash</key> | |
| 181 | + <data> | |
| 182 | + Dfsx9zrg99RidETW6aelsbOnIac= | |
| 183 | + </data> | |
| 184 | + <key>requirement</key> | |
| 185 | + <string>cdhash H"0dfb31f73ae0f7d4627444d6e9a7a5b1b3a721a7"</string> | |
| 186 | + </dict> | |
| 187 | + <key>Frameworks/libcairo-gobject.2.dylib</key> | |
| 188 | + <dict> | |
| 189 | + <key>cdhash</key> | |
| 190 | + <data> | |
| 191 | + Lud1tyfMNY5p8sBrvRPo4vtch2s= | |
| 192 | + </data> | |
| 193 | + <key>requirement</key> | |
| 194 | + <string>cdhash H"2ee775b727cc358e69f2c06bbd13e8e2fb5c876b"</string> | |
| 195 | + </dict> | |
| 196 | + <key>Frameworks/libcairo-script-interpreter.2.dylib</key> | |
| 197 | + <dict> | |
| 198 | + <key>cdhash</key> | |
| 199 | + <data> | |
| 200 | + NnGzyt2knOrMH3OiJCQRd7NRFcY= | |
| 201 | + </data> | |
| 202 | + <key>requirement</key> | |
| 203 | + <string>cdhash H"3671b3cadda49ceacc1f73a224241177b35115c6"</string> | |
| 204 | + </dict> | |
| 205 | + <key>Frameworks/libcairo.2.dylib</key> | |
| 206 | + <dict> | |
| 207 | + <key>cdhash</key> | |
| 208 | + <data> | |
| 209 | + ajyuORlwA5FrIeVuUdF2zje9eWk= | |
| 210 | + </data> | |
| 211 | + <key>requirement</key> | |
| 212 | + <string>cdhash H"6a3cae39197003916b21e56e51d176ce37bd7969"</string> | |
| 213 | + </dict> | |
| 214 | + <key>Frameworks/libepoxy.0.dylib</key> | |
| 215 | + <dict> | |
| 216 | + <key>cdhash</key> | |
| 217 | + <data> | |
| 218 | + Vb243hjxI3niAZSQIm5nHHN+Jqc= | |
| 219 | + </data> | |
| 220 | + <key>requirement</key> | |
| 221 | + <string>cdhash H"55bdb8de18f12379e2019490226e671c737e26a7"</string> | |
| 222 | + </dict> | |
| 223 | + <key>Frameworks/libfontconfig.1.dylib</key> | |
| 224 | + <dict> | |
| 225 | + <key>cdhash</key> | |
| 226 | + <data> | |
| 227 | + mzD8tFzWBMRiq81ySj96aMs1ldA= | |
| 228 | + </data> | |
| 229 | + <key>requirement</key> | |
| 230 | + <string>cdhash H"9b30fcb45cd604c462abcd724a3f7a68cb3595d0"</string> | |
| 231 | + </dict> | |
| 232 | + <key>Frameworks/libfreetype.6.dylib</key> | |
| 233 | + <dict> | |
| 234 | + <key>cdhash</key> | |
| 235 | + <data> | |
| 236 | + g24zAEmY3DJEZBxV1ChACcAKQPk= | |
| 237 | + </data> | |
| 238 | + <key>requirement</key> | |
| 239 | + <string>cdhash H"836e33004998dc3244641c55d4284009c00a40f9"</string> | |
| 240 | + </dict> | |
| 241 | + <key>Frameworks/libfribidi.0.dylib</key> | |
| 242 | + <dict> | |
| 243 | + <key>cdhash</key> | |
| 244 | + <data> | |
| 245 | + f6ZlTzrBxyLa+n3VP8n6TBsLbwc= | |
| 246 | + </data> | |
| 247 | + <key>requirement</key> | |
| 248 | + <string>cdhash H"7fa6654f3ac1c722dafa7dd53fc9fa4c1b0b6f07"</string> | |
| 249 | + </dict> | |
| 250 | + <key>Frameworks/libgcc_s.1.1.dylib</key> | |
| 251 | + <dict> | |
| 252 | + <key>cdhash</key> | |
| 253 | + <data> | |
| 254 | + nvqjNDCOgKVNM1Zfbg4hWL7OgIk= | |
| 255 | + </data> | |
| 256 | + <key>requirement</key> | |
| 257 | + <string>cdhash H"9efaa334308e80a54d33565f6e0e2158bece8089"</string> | |
| 258 | + </dict> | |
| 259 | + <key>Frameworks/libgdk_pixbuf-2.0.0.dylib</key> | |
| 260 | + <dict> | |
| 261 | + <key>cdhash</key> | |
| 262 | + <data> | |
| 263 | + 28DT35Q8ktBkbgVuviLN2NVF8h0= | |
| 264 | + </data> | |
| 265 | + <key>requirement</key> | |
| 266 | + <string>cdhash H"dbc0d3df943c92d0646e056ebe22cdd8d545f21d"</string> | |
| 267 | + </dict> | |
| 268 | + <key>Frameworks/libgfortran.5.dylib</key> | |
| 269 | + <dict> | |
| 270 | + <key>cdhash</key> | |
| 271 | + <data> | |
| 272 | + vYvxI7D4BYYyY0HTiQm49jFvRi8= | |
| 273 | + </data> | |
| 274 | + <key>requirement</key> | |
| 275 | + <string>cdhash H"bd8bf123b0f80586326341d38909b8f6316f462f"</string> | |
| 276 | + </dict> | |
| 277 | + <key>Frameworks/libgio-2.0.0.dylib</key> | |
| 278 | + <dict> | |
| 279 | + <key>cdhash</key> | |
| 280 | + <data> | |
| 281 | + 4+PNWv2Vs+BlZpRm2jYZQ9so/2Y= | |
| 282 | + </data> | |
| 283 | + <key>requirement</key> | |
| 284 | + <string>cdhash H"e3e3cd5afd95b3e065669466da361943db28ff66"</string> | |
| 285 | + </dict> | |
| 286 | + <key>Frameworks/libglib-2.0.0.dylib</key> | |
| 287 | + <dict> | |
| 288 | + <key>cdhash</key> | |
| 289 | + <data> | |
| 290 | + gvtmKbsuTFiWMUI0GPHw+5u4mrI= | |
| 291 | + </data> | |
| 292 | + <key>requirement</key> | |
| 293 | + <string>cdhash H"82fb6629bb2e4c589631423418f1f0fb9bb89ab2"</string> | |
| 294 | + </dict> | |
| 295 | + <key>Frameworks/libgmodule-2.0.0.dylib</key> | |
| 296 | + <dict> | |
| 297 | + <key>cdhash</key> | |
| 298 | + <data> | |
| 299 | + Db3TSMmuehqWC0G1VeUA0nJzxsA= | |
| 300 | + </data> | |
| 301 | + <key>requirement</key> | |
| 302 | + <string>cdhash H"0dbdd348c9ae7a1a960b41b555e500d27273c6c0"</string> | |
| 303 | + </dict> | |
| 304 | + <key>Frameworks/libgobject-2.0.0.dylib</key> | |
| 305 | + <dict> | |
| 306 | + <key>cdhash</key> | |
| 307 | + <data> | |
| 308 | + tdvhcPsqx9b72kdQ8Ca3eQxpkCw= | |
| 309 | + </data> | |
| 310 | + <key>requirement</key> | |
| 311 | + <string>cdhash H"b5dbe170fb2ac7d6fbda4750f026b7790c69902c"</string> | |
| 312 | + </dict> | |
| 313 | + <key>Frameworks/libgraphene-1.0.0.dylib</key> | |
| 314 | + <dict> | |
| 315 | + <key>cdhash</key> | |
| 316 | + <data> | |
| 317 | + elPyvwqxLjgjcg4IiIWOB2lsLrk= | |
| 318 | + </data> | |
| 319 | + <key>requirement</key> | |
| 320 | + <string>cdhash H"7a53f2bf0ab12e3823720e0888858e07696c2eb9"</string> | |
| 321 | + </dict> | |
| 322 | + <key>Frameworks/libgraphite2.3.2.1.dylib</key> | |
| 323 | + <dict> | |
| 324 | + <key>cdhash</key> | |
| 325 | + <data> | |
| 326 | + NtiXuiz1iTnSZaAfC8RPkpuzpQU= | |
| 327 | + </data> | |
| 328 | + <key>requirement</key> | |
| 329 | + <string>cdhash H"36d897ba2cf58939d265a01f0bc44f929bb3a505"</string> | |
| 330 | + </dict> | |
| 331 | + <key>Frameworks/libgtk-4-fortran.4.8.0.dylib</key> | |
| 332 | + <dict> | |
| 333 | + <key>cdhash</key> | |
| 334 | + <data> | |
| 335 | + amz8h9UTGS4xgQTDykG/Pki6w9Q= | |
| 336 | + </data> | |
| 337 | + <key>requirement</key> | |
| 338 | + <string>cdhash H"6a6cfc87d513192e318104c3ca41bf3e48bac3d4"</string> | |
| 339 | + </dict> | |
| 340 | + <key>Frameworks/libgtk-4.1.dylib</key> | |
| 341 | + <dict> | |
| 342 | + <key>cdhash</key> | |
| 343 | + <data> | |
| 344 | + QXHOKwiHh7p6/RNEjnAgkrzXrnM= | |
| 345 | + </data> | |
| 346 | + <key>requirement</key> | |
| 347 | + <string>cdhash H"4171ce2b088787ba7afd13448e702092bcd7ae73"</string> | |
| 348 | + </dict> | |
| 349 | + <key>Frameworks/libharfbuzz-subset.0.dylib</key> | |
| 350 | + <dict> | |
| 351 | + <key>cdhash</key> | |
| 352 | + <data> | |
| 353 | + sLBb5HwRxxun30tPhDx/T+ItFgc= | |
| 354 | + </data> | |
| 355 | + <key>requirement</key> | |
| 356 | + <string>cdhash H"b0b05be47c11c71ba7df4b4f843c7f4fe22d1607"</string> | |
| 357 | + </dict> | |
| 358 | + <key>Frameworks/libharfbuzz.0.dylib</key> | |
| 359 | + <dict> | |
| 360 | + <key>cdhash</key> | |
| 361 | + <data> | |
| 362 | + i/2oLoVTp+pH+qTegSuDx8oiOTw= | |
| 363 | + </data> | |
| 364 | + <key>requirement</key> | |
| 365 | + <string>cdhash H"8bfda82e8553a7ea47faa4de812b83c7ca22393c"</string> | |
| 366 | + </dict> | |
| 367 | + <key>Frameworks/libintl.8.dylib</key> | |
| 368 | + <dict> | |
| 369 | + <key>cdhash</key> | |
| 370 | + <data> | |
| 371 | + OCVnxvDVD+Ebh9UMuDT4L6V1vLs= | |
| 372 | + </data> | |
| 373 | + <key>requirement</key> | |
| 374 | + <string>cdhash H"382567c6f0d50fe11b87d50cb834f82fa575bcbb"</string> | |
| 375 | + </dict> | |
| 376 | + <key>Frameworks/libjpeg.8.3.2.dylib</key> | |
| 377 | + <dict> | |
| 378 | + <key>cdhash</key> | |
| 379 | + <data> | |
| 380 | + FfZP819/6xNN4AkbFKA3NsCU8NI= | |
| 381 | + </data> | |
| 382 | + <key>requirement</key> | |
| 383 | + <string>cdhash H"15f64ff35f7feb134de0091b14a03736c094f0d2"</string> | |
| 384 | + </dict> | |
| 385 | + <key>Frameworks/liblzma.5.dylib</key> | |
| 386 | + <dict> | |
| 387 | + <key>cdhash</key> | |
| 388 | + <data> | |
| 389 | + HQAsQ50CZpB3ERrZwtvb2tW5zsk= | |
| 390 | + </data> | |
| 391 | + <key>requirement</key> | |
| 392 | + <string>cdhash H"1d002c439d02669077111ad9c2dbdbdad5b9cec9"</string> | |
| 393 | + </dict> | |
| 394 | + <key>Frameworks/liblzo2.2.dylib</key> | |
| 395 | + <dict> | |
| 396 | + <key>cdhash</key> | |
| 397 | + <data> | |
| 398 | + t610X2lzaQQ/2kuzYdmsNNM82hU= | |
| 399 | + </data> | |
| 400 | + <key>requirement</key> | |
| 401 | + <string>cdhash H"b7ad745f697369043fda4bb361d9ac34d33cda15"</string> | |
| 402 | + </dict> | |
| 403 | + <key>Frameworks/libpango-1.0.0.dylib</key> | |
| 404 | + <dict> | |
| 405 | + <key>cdhash</key> | |
| 406 | + <data> | |
| 407 | + Hblim3CQFWq3Qy2/UcwkrNQnUT8= | |
| 408 | + </data> | |
| 409 | + <key>requirement</key> | |
| 410 | + <string>cdhash H"1db9629b7090156ab7432dbf51cc24acd427513f"</string> | |
| 411 | + </dict> | |
| 412 | + <key>Frameworks/libpangocairo-1.0.0.dylib</key> | |
| 413 | + <dict> | |
| 414 | + <key>cdhash</key> | |
| 415 | + <data> | |
| 416 | + uiiEy4/uSlZZh7Kurqc3wNOaA8g= | |
| 417 | + </data> | |
| 418 | + <key>requirement</key> | |
| 419 | + <string>cdhash H"ba2884cb8fee4a565987b2aeaea737c0d39a03c8"</string> | |
| 420 | + </dict> | |
| 421 | + <key>Frameworks/libpangoft2-1.0.0.dylib</key> | |
| 422 | + <dict> | |
| 423 | + <key>cdhash</key> | |
| 424 | + <data> | |
| 425 | + X1ee14eTcH7guRPlG55jZyrP2LI= | |
| 426 | + </data> | |
| 427 | + <key>requirement</key> | |
| 428 | + <string>cdhash H"5f579ed78793707ee0b913e51b9e63672acfd8b2"</string> | |
| 429 | + </dict> | |
| 430 | + <key>Frameworks/libpcre2-8.0.dylib</key> | |
| 431 | + <dict> | |
| 432 | + <key>cdhash</key> | |
| 433 | + <data> | |
| 434 | + 3kNOB6FIAWkm+mogHboGxoX492g= | |
| 435 | + </data> | |
| 436 | + <key>requirement</key> | |
| 437 | + <string>cdhash H"de434e07a148016926fa6a201dba06c685f8f768"</string> | |
| 438 | + </dict> | |
| 439 | + <key>Frameworks/libpixman-1.0.dylib</key> | |
| 440 | + <dict> | |
| 441 | + <key>cdhash</key> | |
| 442 | + <data> | |
| 443 | + LQrlgNp2w3iL2XiMtvVjxASREIE= | |
| 444 | + </data> | |
| 445 | + <key>requirement</key> | |
| 446 | + <string>cdhash H"2d0ae580da76c3788bd9788cb6f563c404911081"</string> | |
| 447 | + </dict> | |
| 448 | + <key>Frameworks/libpng16.16.dylib</key> | |
| 449 | + <dict> | |
| 450 | + <key>cdhash</key> | |
| 451 | + <data> | |
| 452 | + mnwXT27cjXdVla/g+vU6/LR2gSQ= | |
| 453 | + </data> | |
| 454 | + <key>requirement</key> | |
| 455 | + <string>cdhash H"9a7c174f6edc8d775595afe0faf53afcb4768124"</string> | |
| 456 | + </dict> | |
| 457 | + <key>Frameworks/libquadmath.0.dylib</key> | |
| 458 | + <dict> | |
| 459 | + <key>cdhash</key> | |
| 460 | + <data> | |
| 461 | + dRya+3oAAimeq6uWbVMO/7pepl8= | |
| 462 | + </data> | |
| 463 | + <key>requirement</key> | |
| 464 | + <string>cdhash H"751c9afb7a0002299eabab966d530effba5ea65f"</string> | |
| 465 | + </dict> | |
| 466 | + <key>Frameworks/librsvg-2.2.dylib</key> | |
| 467 | + <dict> | |
| 468 | + <key>cdhash</key> | |
| 469 | + <data> | |
| 470 | + NkaepcOr4QsGQj/lxCVXaGUJXqo= | |
| 471 | + </data> | |
| 472 | + <key>requirement</key> | |
| 473 | + <string>cdhash H"36469ea5c3abe10b06423fe5c425576865095eaa"</string> | |
| 474 | + </dict> | |
| 475 | + <key>Frameworks/libtiff.6.dylib</key> | |
| 476 | + <dict> | |
| 477 | + <key>cdhash</key> | |
| 478 | + <data> | |
| 479 | + uQcfsNCEYplNkYRLvcqOehW9BJo= | |
| 480 | + </data> | |
| 481 | + <key>requirement</key> | |
| 482 | + <string>cdhash H"b9071fb0d08462994d91844bbdca8e7a15bd049a"</string> | |
| 483 | + </dict> | |
| 484 | + <key>Frameworks/libxcb-render.0.0.0.dylib</key> | |
| 485 | + <dict> | |
| 486 | + <key>cdhash</key> | |
| 487 | + <data> | |
| 488 | + NrLL3e+vcac1VKAa+tiiZSIrI7A= | |
| 489 | + </data> | |
| 490 | + <key>requirement</key> | |
| 491 | + <string>cdhash H"36b2cbddefaf71a73554a01afad8a265222b23b0"</string> | |
| 492 | + </dict> | |
| 493 | + <key>Frameworks/libxcb-shm.0.0.0.dylib</key> | |
| 494 | + <dict> | |
| 495 | + <key>cdhash</key> | |
| 496 | + <data> | |
| 497 | + ae8afTjwpR1ec8E0w2eMUklOouQ= | |
| 498 | + </data> | |
| 499 | + <key>requirement</key> | |
| 500 | + <string>cdhash H"69ef1a7d38f0a51d5e73c134c3678c52494ea2e4"</string> | |
| 501 | + </dict> | |
| 502 | + <key>Frameworks/libxcb.1.1.0.dylib</key> | |
| 503 | + <dict> | |
| 504 | + <key>cdhash</key> | |
| 505 | + <data> | |
| 506 | + +k6hRFyDOmPsxtUgqpQJ9nPWvac= | |
| 507 | + </data> | |
| 508 | + <key>requirement</key> | |
| 509 | + <string>cdhash H"fa4ea1445c833a63ecc6d520aa9409f673d6bda7"</string> | |
| 510 | + </dict> | |
| 511 | + <key>Frameworks/libzstd.1.5.7.dylib</key> | |
| 512 | + <dict> | |
| 513 | + <key>cdhash</key> | |
| 514 | + <data> | |
| 515 | + 1uZ8qwWkT3qJgIo2u38UM5oWYDA= | |
| 516 | + </data> | |
| 517 | + <key>requirement</key> | |
| 518 | + <string>cdhash H"d6e67cab05a44f7a89808a36bb7f14339a166030"</string> | |
| 519 | + </dict> | |
| 520 | + <key>MacOS/sniffly-bin</key> | |
| 521 | + <dict> | |
| 522 | + <key>cdhash</key> | |
| 523 | + <data> | |
| 524 | + UnRkFSQSV/ohuGYufIEZd3ovoAU= | |
| 525 | + </data> | |
| 526 | + <key>requirement</key> | |
| 527 | + <string>cdhash H"52746415241257fa21b8662e7c8119777a2fa005"</string> | |
| 528 | + </dict> | |
| 529 | + <key>Resources/share/glib-2.0/schemas/gschema.dtd</key> | |
| 530 | + <dict> | |
| 531 | + <key>hash</key> | |
| 532 | + <data> | |
| 533 | + BY2rQNdph/3FfTHf7nnlVZ+U2/0= | |
| 534 | + </data> | |
| 535 | + <key>hash2</key> | |
| 536 | + <data> | |
| 537 | + RKEvM5BCo/VWipzIa7YX+rr0olg2uMlRCqneIkMZMMY= | |
| 538 | + </data> | |
| 539 | + </dict> | |
| 540 | + <key>Resources/share/glib-2.0/schemas/gschemas.compiled</key> | |
| 541 | + <dict> | |
| 542 | + <key>hash</key> | |
| 543 | + <data> | |
| 544 | + PUbZavbyH7VUpsQKezAGbofeWEo= | |
| 545 | + </data> | |
| 546 | + <key>hash2</key> | |
| 547 | + <data> | |
| 548 | + 6Fzs8WxqLP/mND0xgrewHAT6Mj/fzW0FJtkhIHkn48s= | |
| 549 | + </data> | |
| 550 | + </dict> | |
| 551 | + <key>Resources/share/glib-2.0/schemas/org.gtk.Demo4.gschema.xml</key> | |
| 552 | + <dict> | |
| 553 | + <key>hash</key> | |
| 554 | + <data> | |
| 555 | + iFjAPaAxzf6zp4IKKXUsaS2Wi8g= | |
| 556 | + </data> | |
| 557 | + <key>hash2</key> | |
| 558 | + <data> | |
| 559 | + aOHHxecsalK9xqeh7blQzlfmRoJOOeVasU8ocy4WS+Y= | |
| 560 | + </data> | |
| 561 | + </dict> | |
| 562 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Inspector.gschema.xml</key> | |
| 563 | + <dict> | |
| 564 | + <key>hash</key> | |
| 565 | + <data> | |
| 566 | + jgzjvDTQrcwV4hrD/HgQ+N4AFhM= | |
| 567 | + </data> | |
| 568 | + <key>hash2</key> | |
| 569 | + <data> | |
| 570 | + CnFkVsVv9qTzrR1ysiBbLpgqIxqVhsujK8fRvMk89Fo= | |
| 571 | + </data> | |
| 572 | + </dict> | |
| 573 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.ColorChooser.gschema.xml</key> | |
| 574 | + <dict> | |
| 575 | + <key>hash</key> | |
| 576 | + <data> | |
| 577 | + /iNWMcJOzPva8L3ulwQ6HwtRgXM= | |
| 578 | + </data> | |
| 579 | + <key>hash2</key> | |
| 580 | + <data> | |
| 581 | + PGCgIKyCOtnAI5TIV5KWcRoUj7bhXawcTf+jRwMDrzI= | |
| 582 | + </data> | |
| 583 | + </dict> | |
| 584 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.Debug.gschema.xml</key> | |
| 585 | + <dict> | |
| 586 | + <key>hash</key> | |
| 587 | + <data> | |
| 588 | + qL7MxCdaSI3mj4iLFTPn1m7nrrw= | |
| 589 | + </data> | |
| 590 | + <key>hash2</key> | |
| 591 | + <data> | |
| 592 | + 8wk7AOkYShkonwdVNF40FJIar+FUMzQrNrMnreIEiZg= | |
| 593 | + </data> | |
| 594 | + </dict> | |
| 595 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.EmojiChooser.gschema.xml</key> | |
| 596 | + <dict> | |
| 597 | + <key>hash</key> | |
| 598 | + <data> | |
| 599 | + PJHFmo5OoI3zUvKllylBp0gALEE= | |
| 600 | + </data> | |
| 601 | + <key>hash2</key> | |
| 602 | + <data> | |
| 603 | + iTsmBWnoMruWv6X/lYk/hdgVQwaXmfIr9m3UGa1QpwI= | |
| 604 | + </data> | |
| 605 | + </dict> | |
| 606 | + <key>Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.FileChooser.gschema.xml</key> | |
| 607 | + <dict> | |
| 608 | + <key>hash</key> | |
| 609 | + <data> | |
| 610 | + U9fKvSUl9DJ/cEdecFSvzRxA16M= | |
| 611 | + </data> | |
| 612 | + <key>hash2</key> | |
| 613 | + <data> | |
| 614 | + 56dIL3k3oFiJYWE98rFOUJsIwSgb2wsSqyeYVtfY/Oo= | |
| 615 | + </data> | |
| 616 | + </dict> | |
| 617 | + <key>Resources/share/gtk-4.0/emoji/bn.gresource</key> | |
| 618 | + <dict> | |
| 619 | + <key>hash</key> | |
| 620 | + <data> | |
| 621 | + Tt0UUyhxmAqGPDQhIghAnO1//cE= | |
| 622 | + </data> | |
| 623 | + <key>hash2</key> | |
| 624 | + <data> | |
| 625 | + eVCtdtJdwWHuwPvMRr8ymM5dcWPgxNGXaqkY1T3PjVs= | |
| 626 | + </data> | |
| 627 | + </dict> | |
| 628 | + <key>Resources/share/gtk-4.0/emoji/da.gresource</key> | |
| 629 | + <dict> | |
| 630 | + <key>hash</key> | |
| 631 | + <data> | |
| 632 | + qofpYMTzCx5rurhSCQ49Eq/gQd4= | |
| 633 | + </data> | |
| 634 | + <key>hash2</key> | |
| 635 | + <data> | |
| 636 | + e7wK1U9wYuMzYVqvl8yIKS68cnjKV/itZpgwOQC6K2k= | |
| 637 | + </data> | |
| 638 | + </dict> | |
| 639 | + <key>Resources/share/gtk-4.0/emoji/de.gresource</key> | |
| 640 | + <dict> | |
| 641 | + <key>hash</key> | |
| 642 | + <data> | |
| 643 | + UiZSMGYJhJI45AzwpH2uNu0I19M= | |
| 644 | + </data> | |
| 645 | + <key>hash2</key> | |
| 646 | + <data> | |
| 647 | + SiT2BW+MaH9dn/fiwakcBlc+PkEPt+Ri3tPQOkADpFQ= | |
| 648 | + </data> | |
| 649 | + </dict> | |
| 650 | + <key>Resources/share/gtk-4.0/emoji/es.gresource</key> | |
| 651 | + <dict> | |
| 652 | + <key>hash</key> | |
| 653 | + <data> | |
| 654 | + b4UyvR+WYMy2I1vuC9+0cH46cj8= | |
| 655 | + </data> | |
| 656 | + <key>hash2</key> | |
| 657 | + <data> | |
| 658 | + 3imS6uyMeTfGIP4/M9mVTupvlL7+0eUqTe0WiLo10a8= | |
| 659 | + </data> | |
| 660 | + </dict> | |
| 661 | + <key>Resources/share/gtk-4.0/emoji/et.gresource</key> | |
| 662 | + <dict> | |
| 663 | + <key>hash</key> | |
| 664 | + <data> | |
| 665 | + bhg3hLjbjGntSbc6hpUGyIJBIxc= | |
| 666 | + </data> | |
| 667 | + <key>hash2</key> | |
| 668 | + <data> | |
| 669 | + aFJrcw8R95AMI15xDh69RS4duL0ZdPDgyGcDE0qFVoE= | |
| 670 | + </data> | |
| 671 | + </dict> | |
| 672 | + <key>Resources/share/gtk-4.0/emoji/fi.gresource</key> | |
| 673 | + <dict> | |
| 674 | + <key>hash</key> | |
| 675 | + <data> | |
| 676 | + jlJsJG7RP0tZypyLFafqVgSUsUc= | |
| 677 | + </data> | |
| 678 | + <key>hash2</key> | |
| 679 | + <data> | |
| 680 | + qHJFplneMnFm9d5bVbG9d+upcSoU0SwnT+loluAFZOc= | |
| 681 | + </data> | |
| 682 | + </dict> | |
| 683 | + <key>Resources/share/gtk-4.0/emoji/fr.gresource</key> | |
| 684 | + <dict> | |
| 685 | + <key>hash</key> | |
| 686 | + <data> | |
| 687 | + eTI1ovErYI/kFxgga0MlceFwxAY= | |
| 688 | + </data> | |
| 689 | + <key>hash2</key> | |
| 690 | + <data> | |
| 691 | + WB5hhy485ylC1HQXRXHpUbj3bNj97nF+aSxOLPwMFm0= | |
| 692 | + </data> | |
| 693 | + </dict> | |
| 694 | + <key>Resources/share/gtk-4.0/emoji/hi.gresource</key> | |
| 695 | + <dict> | |
| 696 | + <key>hash</key> | |
| 697 | + <data> | |
| 698 | + I3Ur/v5aarS3QPeCQkOPobCq8t4= | |
| 699 | + </data> | |
| 700 | + <key>hash2</key> | |
| 701 | + <data> | |
| 702 | + 4MNL6PLEDrRhUp0Ad08PkzEneUjuj3pnklcag2cu5lQ= | |
| 703 | + </data> | |
| 704 | + </dict> | |
| 705 | + <key>Resources/share/gtk-4.0/emoji/hu.gresource</key> | |
| 706 | + <dict> | |
| 707 | + <key>hash</key> | |
| 708 | + <data> | |
| 709 | + xlwpk+cy3V7DReSYDiXPb7WIatk= | |
| 710 | + </data> | |
| 711 | + <key>hash2</key> | |
| 712 | + <data> | |
| 713 | + wGpSXfPARQkHd0FziRluM5e+dPOn08AxosV9yKLP5aE= | |
| 714 | + </data> | |
| 715 | + </dict> | |
| 716 | + <key>Resources/share/gtk-4.0/emoji/it.gresource</key> | |
| 717 | + <dict> | |
| 718 | + <key>hash</key> | |
| 719 | + <data> | |
| 720 | + nwlNj5cx3HGmPLlEIoZch9538ZQ= | |
| 721 | + </data> | |
| 722 | + <key>hash2</key> | |
| 723 | + <data> | |
| 724 | + id9nXjNvF+gQG6etlz1mAbdqDjYJ3XvMjNeJ2KQ6lGE= | |
| 725 | + </data> | |
| 726 | + </dict> | |
| 727 | + <key>Resources/share/gtk-4.0/emoji/ja.gresource</key> | |
| 728 | + <dict> | |
| 729 | + <key>hash</key> | |
| 730 | + <data> | |
| 731 | + cvOWCi+1srwn1yX8CAuDqO4wI0o= | |
| 732 | + </data> | |
| 733 | + <key>hash2</key> | |
| 734 | + <data> | |
| 735 | + YP+KgJdlz+D9V7T/sUwwERdWGC1BY3LdeN0vsGhu+ZE= | |
| 736 | + </data> | |
| 737 | + </dict> | |
| 738 | + <key>Resources/share/gtk-4.0/emoji/ko.gresource</key> | |
| 739 | + <dict> | |
| 740 | + <key>hash</key> | |
| 741 | + <data> | |
| 742 | + bKjuMyXqmqLWm+A6RQCmmoJe3jA= | |
| 743 | + </data> | |
| 744 | + <key>hash2</key> | |
| 745 | + <data> | |
| 746 | + YnbYZkhmCri/4IaslHCnTUVj5wxjanKyUMqwZIwrtco= | |
| 747 | + </data> | |
| 748 | + </dict> | |
| 749 | + <key>Resources/share/gtk-4.0/emoji/lt.gresource</key> | |
| 750 | + <dict> | |
| 751 | + <key>hash</key> | |
| 752 | + <data> | |
| 753 | + LMQx/lMs3iwIKzc1w4CfuueNjEA= | |
| 754 | + </data> | |
| 755 | + <key>hash2</key> | |
| 756 | + <data> | |
| 757 | + mLFpHPxoG7PdDbjMPNWerDKTrMd2tRjSp+oHDxs5C3M= | |
| 758 | + </data> | |
| 759 | + </dict> | |
| 760 | + <key>Resources/share/gtk-4.0/emoji/ms.gresource</key> | |
| 761 | + <dict> | |
| 762 | + <key>hash</key> | |
| 763 | + <data> | |
| 764 | + X3/AN4eWGYG/dq4w8SExYqTwG70= | |
| 765 | + </data> | |
| 766 | + <key>hash2</key> | |
| 767 | + <data> | |
| 768 | + WO+oNp2J+0MiNS2qEMVY3XtSGnTtsybmDe3ILHIT8Ls= | |
| 769 | + </data> | |
| 770 | + </dict> | |
| 771 | + <key>Resources/share/gtk-4.0/emoji/nb.gresource</key> | |
| 772 | + <dict> | |
| 773 | + <key>hash</key> | |
| 774 | + <data> | |
| 775 | + FyeaCXibNgtT4H2/v94AqcN4P9Q= | |
| 776 | + </data> | |
| 777 | + <key>hash2</key> | |
| 778 | + <data> | |
| 779 | + h/l3JQk5DFuOWnDryYZjNJNN/e9LtLkFecA01wLH9/I= | |
| 780 | + </data> | |
| 781 | + </dict> | |
| 782 | + <key>Resources/share/gtk-4.0/emoji/nl.gresource</key> | |
| 783 | + <dict> | |
| 784 | + <key>hash</key> | |
| 785 | + <data> | |
| 786 | + 1JRe4GqcXqPIz0elmFY6sgvKcxU= | |
| 787 | + </data> | |
| 788 | + <key>hash2</key> | |
| 789 | + <data> | |
| 790 | + cM714QsReGOYu2Om3Oohzjhi5bn68A+8/j/MutaGQdw= | |
| 791 | + </data> | |
| 792 | + </dict> | |
| 793 | + <key>Resources/share/gtk-4.0/emoji/pl.gresource</key> | |
| 794 | + <dict> | |
| 795 | + <key>hash</key> | |
| 796 | + <data> | |
| 797 | + pUi/VXwKnYDW6vW1XFDYpbcDPM8= | |
| 798 | + </data> | |
| 799 | + <key>hash2</key> | |
| 800 | + <data> | |
| 801 | + TG3SiVmFRu0OMYuneTqPGOfBd/ytsdnXJLVyZTqyMFc= | |
| 802 | + </data> | |
| 803 | + </dict> | |
| 804 | + <key>Resources/share/gtk-4.0/emoji/pt.gresource</key> | |
| 805 | + <dict> | |
| 806 | + <key>hash</key> | |
| 807 | + <data> | |
| 808 | + ++WVXFGgVBcz+mw2tAW3jlQnRTI= | |
| 809 | + </data> | |
| 810 | + <key>hash2</key> | |
| 811 | + <data> | |
| 812 | + Ne4kJw5Ttd7KhPPalVEDc+z1qOXdaUF/I5Qth9TtOo8= | |
| 813 | + </data> | |
| 814 | + </dict> | |
| 815 | + <key>Resources/share/gtk-4.0/emoji/ru.gresource</key> | |
| 816 | + <dict> | |
| 817 | + <key>hash</key> | |
| 818 | + <data> | |
| 819 | + 20h8jXi2Xl/CpqDgSDFvFW4tgW8= | |
| 820 | + </data> | |
| 821 | + <key>hash2</key> | |
| 822 | + <data> | |
| 823 | + tIajLQZv1OGyuu+87wxf7/bgAQpI6f51T5Ikaz6lyIk= | |
| 824 | + </data> | |
| 825 | + </dict> | |
| 826 | + <key>Resources/share/gtk-4.0/emoji/sv.gresource</key> | |
| 827 | + <dict> | |
| 828 | + <key>hash</key> | |
| 829 | + <data> | |
| 830 | + ZIgYlHeEl4zkH9DZ0tTFAV7fZCY= | |
| 831 | + </data> | |
| 832 | + <key>hash2</key> | |
| 833 | + <data> | |
| 834 | + b3jgoHgKCA0QOdsSrrZ7ePZ3n3ZMYGROlilGU269a5Q= | |
| 835 | + </data> | |
| 836 | + </dict> | |
| 837 | + <key>Resources/share/gtk-4.0/emoji/th.gresource</key> | |
| 838 | + <dict> | |
| 839 | + <key>hash</key> | |
| 840 | + <data> | |
| 841 | + XOao2Fz+KGA62HJPY1Zz6TrQQPg= | |
| 842 | + </data> | |
| 843 | + <key>hash2</key> | |
| 844 | + <data> | |
| 845 | + swvnsTHEFso3hO7RNl0Q82P49anNjcjLWZ6BTwX2/4I= | |
| 846 | + </data> | |
| 847 | + </dict> | |
| 848 | + <key>Resources/share/gtk-4.0/emoji/uk.gresource</key> | |
| 849 | + <dict> | |
| 850 | + <key>hash</key> | |
| 851 | + <data> | |
| 852 | + f5mTp7g47Tu5E6uxfb/Hje+aoYE= | |
| 853 | + </data> | |
| 854 | + <key>hash2</key> | |
| 855 | + <data> | |
| 856 | + 0rzLvDsqBLat/HtRD6bq0CzolVd/XcCNjARnKt8mbUY= | |
| 857 | + </data> | |
| 858 | + </dict> | |
| 859 | + <key>Resources/share/gtk-4.0/emoji/zh.gresource</key> | |
| 860 | + <dict> | |
| 861 | + <key>hash</key> | |
| 862 | + <data> | |
| 863 | + acEBrz5jBFRfrrlMYH7TY2Dt5CY= | |
| 864 | + </data> | |
| 865 | + <key>hash2</key> | |
| 866 | + <data> | |
| 867 | + zw6veg/zYZ0niQBOCUWPWVjCnq3Voqu588iPt0Cdz1A= | |
| 868 | + </data> | |
| 869 | + </dict> | |
| 870 | + <key>Resources/share/gtk-4.0/gtk4builder.rng</key> | |
| 871 | + <dict> | |
| 872 | + <key>hash</key> | |
| 873 | + <data> | |
| 874 | + X93vrJ9nzUWc1PDsKhmRjfy5Q+U= | |
| 875 | + </data> | |
| 876 | + <key>hash2</key> | |
| 877 | + <data> | |
| 878 | + wrrOUrTZshDzoqFV6g+m32Gp1wFQ7Q+C31tCYNGRzXI= | |
| 879 | + </data> | |
| 880 | + </dict> | |
| 881 | + <key>Resources/share/gtk-4.0/valgrind/gtk.supp</key> | |
| 882 | + <dict> | |
| 883 | + <key>hash</key> | |
| 884 | + <data> | |
| 885 | + 6B5NUN/9YTSLoE1eHra2FDmNNGE= | |
| 886 | + </data> | |
| 887 | + <key>hash2</key> | |
| 888 | + <data> | |
| 889 | + bf5KnWpZJgMScRn7Gy9aQOgVYKbCDWuq+n6oo/w/FjQ= | |
| 890 | + </data> | |
| 891 | + </dict> | |
| 892 | + </dict> | |
| 893 | + <key>rules</key> | |
| 894 | + <dict> | |
| 895 | + <key>^Resources/</key> | |
| 896 | + <true/> | |
| 897 | + <key>^Resources/.*\.lproj/</key> | |
| 898 | + <dict> | |
| 899 | + <key>optional</key> | |
| 900 | + <true/> | |
| 901 | + <key>weight</key> | |
| 902 | + <real>1000</real> | |
| 903 | + </dict> | |
| 904 | + <key>^Resources/.*\.lproj/locversion.plist$</key> | |
| 905 | + <dict> | |
| 906 | + <key>omit</key> | |
| 907 | + <true/> | |
| 908 | + <key>weight</key> | |
| 909 | + <real>1100</real> | |
| 910 | + </dict> | |
| 911 | + <key>^Resources/Base\.lproj/</key> | |
| 912 | + <dict> | |
| 913 | + <key>weight</key> | |
| 914 | + <real>1010</real> | |
| 915 | + </dict> | |
| 916 | + <key>^version.plist$</key> | |
| 917 | + <true/> | |
| 918 | + </dict> | |
| 919 | + <key>rules2</key> | |
| 920 | + <dict> | |
| 921 | + <key>.*\.dSYM($|/)</key> | |
| 922 | + <dict> | |
| 923 | + <key>weight</key> | |
| 924 | + <real>11</real> | |
| 925 | + </dict> | |
| 926 | + <key>^(.*/)?\.DS_Store$</key> | |
| 927 | + <dict> | |
| 928 | + <key>omit</key> | |
| 929 | + <true/> | |
| 930 | + <key>weight</key> | |
| 931 | + <real>2000</real> | |
| 932 | + </dict> | |
| 933 | + <key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key> | |
| 934 | + <dict> | |
| 935 | + <key>nested</key> | |
| 936 | + <true/> | |
| 937 | + <key>weight</key> | |
| 938 | + <real>10</real> | |
| 939 | + </dict> | |
| 940 | + <key>^.*</key> | |
| 941 | + <true/> | |
| 942 | + <key>^Info\.plist$</key> | |
| 943 | + <dict> | |
| 944 | + <key>omit</key> | |
| 945 | + <true/> | |
| 946 | + <key>weight</key> | |
| 947 | + <real>20</real> | |
| 948 | + </dict> | |
| 949 | + <key>^PkgInfo$</key> | |
| 950 | + <dict> | |
| 951 | + <key>omit</key> | |
| 952 | + <true/> | |
| 953 | + <key>weight</key> | |
| 954 | + <real>20</real> | |
| 955 | + </dict> | |
| 956 | + <key>^Resources/</key> | |
| 957 | + <dict> | |
| 958 | + <key>weight</key> | |
| 959 | + <real>20</real> | |
| 960 | + </dict> | |
| 961 | + <key>^Resources/.*\.lproj/</key> | |
| 962 | + <dict> | |
| 963 | + <key>optional</key> | |
| 964 | + <true/> | |
| 965 | + <key>weight</key> | |
| 966 | + <real>1000</real> | |
| 967 | + </dict> | |
| 968 | + <key>^Resources/.*\.lproj/locversion.plist$</key> | |
| 969 | + <dict> | |
| 970 | + <key>omit</key> | |
| 971 | + <true/> | |
| 972 | + <key>weight</key> | |
| 973 | + <real>1100</real> | |
| 974 | + </dict> | |
| 975 | + <key>^Resources/Base\.lproj/</key> | |
| 976 | + <dict> | |
| 977 | + <key>weight</key> | |
| 978 | + <real>1010</real> | |
| 979 | + </dict> | |
| 980 | + <key>^[^/]+$</key> | |
| 981 | + <dict> | |
| 982 | + <key>nested</key> | |
| 983 | + <true/> | |
| 984 | + <key>weight</key> | |
| 985 | + <real>10</real> | |
| 986 | + </dict> | |
| 987 | + <key>^embedded\.provisionprofile$</key> | |
| 988 | + <dict> | |
| 989 | + <key>weight</key> | |
| 990 | + <real>20</real> | |
| 991 | + </dict> | |
| 992 | + <key>^version\.plist$</key> | |
| 993 | + <dict> | |
| 994 | + <key>weight</key> | |
| 995 | + <real>20</real> | |
| 996 | + </dict> | |
| 997 | + </dict> | |
| 998 | +</dict> | |
| 999 | +</plist> | |
Sniffly.app/Contents/_CodeSignature/CodeSignatureaddedTECHNICAL_STACK.mdadded@@ -0,0 +1,656 @@ | ||
| 1 | +# Sniffly Technical Stack Deep Dive | |
| 2 | + | |
| 3 | +## Core Stack Justification | |
| 4 | + | |
| 5 | +### Why Pure Fortran? | |
| 6 | + | |
| 7 | +**Philosophy:** Prove that Fortran can build modern GUI applications that rival commercial software written in C++/C#. | |
| 8 | + | |
| 9 | +**Practical Advantages:** | |
| 10 | +1. **Performance:** Native compilation, no VM/interpreter overhead | |
| 11 | +2. **Memory Safety:** Strong typing, bounds checking (with compiler flags) | |
| 12 | +3. **Existing Code:** Leverage proven algorithms from sniffert | |
| 13 | +4. **Mathematical Operations:** Natural for size calculations, layout algorithms | |
| 14 | +5. **Array Operations:** Efficient bulk operations on file trees | |
| 15 | + | |
| 16 | +**Challenges Addressed:** | |
| 17 | +- String handling: Use allocatable character arrays (Fortran 2003+) | |
| 18 | +- Pointers: Use Fortran's C interoperability for GTK callbacks | |
| 19 | +- OOP: Use Fortran 2008 type-bound procedures where beneficial | |
| 20 | + | |
| 21 | +--- | |
| 22 | + | |
| 23 | +## GTK4 via gtk-fortran | |
| 24 | + | |
| 25 | +### Why GTK4 (not Qt, wxWidgets, etc.)? | |
| 26 | + | |
| 27 | +**Technical Reasons:** | |
| 28 | +1. **gtk-fortran exists:** Mature, maintained Fortran bindings | |
| 29 | +2. **Native on Linux:** GTK is the de facto standard | |
| 30 | +3. **macOS Support:** GTK4 uses native Cocoa backend | |
| 31 | +4. **Modern:** GTK4 is actively developed, not legacy | |
| 32 | +5. **Cairo Integration:** Built-in 2D drawing library | |
| 33 | + | |
| 34 | +**Comparison to Alternatives:** | |
| 35 | + | |
| 36 | +| Framework | Fortran Bindings | Native Look | Performance | Verdict | | |
| 37 | +|-----------|-----------------|-------------|-------------|---------| | |
| 38 | +| GTK4 | gtk-fortran | Yes (Linux/macOS) | Excellent | ✅ Chosen | | |
| 39 | +| Qt | None (would need C++ wrapper) | Yes | Excellent | ❌ No bindings | | |
| 40 | +| wxWidgets | None | Yes | Good | ❌ No bindings | | |
| 41 | +| FLTK | None | No | Excellent | ❌ No bindings, dated look | | |
| 42 | +| Tk | None (would need Tcl) | No | Poor | ❌ Dated, slow | | |
| 43 | + | |
| 44 | +**gtk-fortran Maturity:** | |
| 45 | +- First release: 2011 | |
| 46 | +- Current version: 3.24.41 (2024) | |
| 47 | +- Active maintainer: Vincent Magnin | |
| 48 | +- Production use: Scientific visualization tools | |
| 49 | +- Platform support: Linux, macOS, FreeBSD, Windows (MSYS2) | |
| 50 | + | |
| 51 | +### GTK4 Architecture | |
| 52 | + | |
| 53 | +``` | |
| 54 | +┌─────────────────────────────────────────────────────────┐ | |
| 55 | +│ Your Fortran Code │ | |
| 56 | +│ (sniffly modules) │ | |
| 57 | +└─────────────────────────────────────────────────────────┘ | |
| 58 | + │ | |
| 59 | + ↓ (iso_c_binding) | |
| 60 | +┌─────────────────────────────────────────────────────────┐ | |
| 61 | +│ gtk-fortran │ | |
| 62 | +│ (Fortran interfaces to GTK C functions) │ | |
| 63 | +└─────────────────────────────────────────────────────────┘ | |
| 64 | + │ | |
| 65 | + ↓ (FFI) | |
| 66 | +┌─────────────────────────────────────────────────────────┐ | |
| 67 | +│ GTK4 C Library │ | |
| 68 | +│ ┌─────────────┬──────────────┬────────────────────┐ │ | |
| 69 | +│ │ Widgets │ GDK │ GSK │ │ | |
| 70 | +│ │ (buttons, │ (events, │ (GPU rendering) │ │ | |
| 71 | +│ │ windows) │ input) │ │ │ | |
| 72 | +│ └─────────────┴──────────────┴────────────────────┘ │ | |
| 73 | +└─────────────────────────────────────────────────────────┘ | |
| 74 | + │ | |
| 75 | + ↓ | |
| 76 | +┌─────────────────────────────────────────────────────────┐ | |
| 77 | +│ Platform Backend │ | |
| 78 | +│ Linux: Wayland/X11 macOS: Cocoa BSD: X11 │ | |
| 79 | +└─────────────────────────────────────────────────────────┘ | |
| 80 | +``` | |
| 81 | + | |
| 82 | +--- | |
| 83 | + | |
| 84 | +## Cairo for 2D Graphics | |
| 85 | + | |
| 86 | +### Why Cairo? | |
| 87 | + | |
| 88 | +**Technical Strengths:** | |
| 89 | +1. **Vector Graphics:** Resolution-independent rendering | |
| 90 | +2. **Anti-aliasing:** Beautiful, smooth edges | |
| 91 | +3. **Hardware Acceleration:** GPU-backed on modern systems | |
| 92 | +4. **Compositing:** Alpha blending, gradients, patterns | |
| 93 | +5. **Text Rendering:** Pango integration for complex text | |
| 94 | + | |
| 95 | +**Cairo Features We'll Use:** | |
| 96 | + | |
| 97 | +| Feature | Usage in Sniffly | | |
| 98 | +|---------|-----------------| | |
| 99 | +| `cairo_rectangle()` | Draw treemap rectangles | | |
| 100 | +| `cairo_fill()` | Fill rectangles with color | | |
| 101 | +| `cairo_stroke()` | Draw borders | | |
| 102 | +| `cairo_set_source_rgb()` | Set solid colors | | |
| 103 | +| `cairo_pattern_create_linear()` | Cushion gradients | | |
| 104 | +| `cairo_clip()` | Clip nested rectangles | | |
| 105 | +| `cairo_save()`/`restore()` | State management | | |
| 106 | +| Pango integration | Render file names | | |
| 107 | + | |
| 108 | +**Performance Characteristics:** | |
| 109 | +- **Fast Paths:** Solid fills, axis-aligned rectangles | |
| 110 | +- **Slow Paths:** Bezier curves, complex paths (we won't use) | |
| 111 | +- **Caching:** Cairo surfaces can be cached | |
| 112 | +- **Expected Performance:** 60fps for 1000s of rectangles | |
| 113 | + | |
| 114 | +### Cairo vs. OpenGL | |
| 115 | + | |
| 116 | +We chose Cairo over OpenGL because: | |
| 117 | +- **Simpler API:** 2D-focused, easier to learn | |
| 118 | +- **Better Integration:** Built into GTK4 | |
| 119 | +- **Text Rendering:** Pango support out of the box | |
| 120 | +- **Sufficient Performance:** 60fps achievable for our use case | |
| 121 | + | |
| 122 | +If performance becomes an issue (unlikely), we can: | |
| 123 | +1. Use GTK4's GSK (GPU Scene Kit) backend | |
| 124 | +2. Batch render calls | |
| 125 | +3. Implement viewport culling (only draw visible nodes) | |
| 126 | + | |
| 127 | +--- | |
| 128 | + | |
| 129 | +## Pango for Text | |
| 130 | + | |
| 131 | +### Why Pango? | |
| 132 | + | |
| 133 | +**Features:** | |
| 134 | +1. **Font Management:** System font detection, fallbacks | |
| 135 | +2. **Text Layout:** Ellipsization, wrapping, alignment | |
| 136 | +3. **Internationalization:** Full Unicode support, RTL text | |
| 137 | +4. **Integration:** Native Cairo rendering | |
| 138 | + | |
| 139 | +**Pango Features We'll Use:** | |
| 140 | + | |
| 141 | +```fortran | |
| 142 | +! Create Pango layout | |
| 143 | +layout = pango_cairo_create_layout(cairo_context) | |
| 144 | + | |
| 145 | +! Set text | |
| 146 | +call pango_layout_set_text(layout, c_str("filename.txt")) | |
| 147 | + | |
| 148 | +! Set font | |
| 149 | +font_desc = pango_font_description_from_string(c_str("Sans 10")) | |
| 150 | +call pango_layout_set_font_description(layout, font_desc) | |
| 151 | + | |
| 152 | +! Set ellipsization for long text | |
| 153 | +call pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END) | |
| 154 | +call pango_layout_set_width(layout, width * PANGO_SCALE) | |
| 155 | + | |
| 156 | +! Render | |
| 157 | +call pango_cairo_show_layout(cairo_context, layout) | |
| 158 | +``` | |
| 159 | + | |
| 160 | +--- | |
| 161 | + | |
| 162 | +## GLib for Core Utilities | |
| 163 | + | |
| 164 | +### Threading | |
| 165 | + | |
| 166 | +**GLib Threads** (not pthreads) for portability: | |
| 167 | + | |
| 168 | +```fortran | |
| 169 | +! Start background scan thread | |
| 170 | +thread = g_thread_new(c_str("scanner"), c_funloc(scan_thread_func), c_loc(scan_data)) | |
| 171 | + | |
| 172 | +! Thread-safe UI updates from background thread | |
| 173 | +call g_idle_add(c_funloc(update_ui_callback), c_loc(progress_data)) | |
| 174 | +``` | |
| 175 | + | |
| 176 | +**Why g_idle_add?** | |
| 177 | +- GTK is **not thread-safe** | |
| 178 | +- g_idle_add queues callback on main thread | |
| 179 | +- Callback runs in GTK event loop | |
| 180 | +- Safe to update UI from callback | |
| 181 | + | |
| 182 | +### Event Loop | |
| 183 | + | |
| 184 | +GTK's main loop handles: | |
| 185 | +- User input (mouse, keyboard) | |
| 186 | +- Window events (resize, close) | |
| 187 | +- Timers and idle callbacks | |
| 188 | +- Background task callbacks (g_idle_add) | |
| 189 | + | |
| 190 | +```fortran | |
| 191 | +! Start GTK main loop (blocks until app quits) | |
| 192 | +call gtk_main() | |
| 193 | +``` | |
| 194 | + | |
| 195 | +### GLib Data Structures | |
| 196 | + | |
| 197 | +We'll primarily use Fortran's native arrays, but GLib provides: | |
| 198 | +- `GList`/`GSList`: Linked lists (if needed for dynamic UI) | |
| 199 | +- `GHashTable`: Hash maps (for quick file lookup) | |
| 200 | +- `GString`: Dynamic strings (if Fortran strings insufficient) | |
| 201 | + | |
| 202 | +--- | |
| 203 | + | |
| 204 | +## Build System: Meson | |
| 205 | + | |
| 206 | +### Why Meson (not Make, CMake, FPM)? | |
| 207 | + | |
| 208 | +**Meson Advantages:** | |
| 209 | +1. **GTK Standard:** All GTK projects use Meson | |
| 210 | +2. **Dependency Detection:** Auto-finds GTK, Cairo, etc. via pkg-config | |
| 211 | +3. **Fast:** Ninja backend, parallel builds | |
| 212 | +4. **Cross-Platform:** Works on Linux, macOS, BSD | |
| 213 | +5. **Clean Syntax:** Python-like, easy to read | |
| 214 | + | |
| 215 | +**Meson vs. FPM:** | |
| 216 | +- FPM is great for pure Fortran projects | |
| 217 | +- FPM doesn't handle C library dependencies well | |
| 218 | +- We'll provide **both** Meson (primary) and FPM (fallback) builds | |
| 219 | + | |
| 220 | +**Sample meson.build:** | |
| 221 | + | |
| 222 | +```meson | |
| 223 | +project('sniffly', 'fortran', | |
| 224 | + version: '1.0.0', | |
| 225 | + default_options: ['warning_level=3']) | |
| 226 | + | |
| 227 | +# Dependencies | |
| 228 | +gtk4_dep = dependency('gtk4') | |
| 229 | +cairo_dep = dependency('cairo') | |
| 230 | +pango_dep = dependency('pango') | |
| 231 | + | |
| 232 | +# Source files | |
| 233 | +src = [ | |
| 234 | + 'src/core/types.f90', | |
| 235 | + 'src/core/file_system.f90', | |
| 236 | + 'src/core/disk_scanner.f90', | |
| 237 | + 'src/layout/squarified.f90', | |
| 238 | + 'src/layout/cushioned.f90', | |
| 239 | + 'src/gui/gtk_app.f90', | |
| 240 | + 'src/rendering/cairo_renderer.f90', | |
| 241 | + 'app/main.f90', | |
| 242 | +] | |
| 243 | + | |
| 244 | +# Executable | |
| 245 | +executable('sniffly', src, | |
| 246 | + dependencies: [gtk4_dep, cairo_dep, pango_dep], | |
| 247 | + install: true) | |
| 248 | +``` | |
| 249 | + | |
| 250 | +**Building:** | |
| 251 | +```bash | |
| 252 | +meson setup build | |
| 253 | +meson compile -C build | |
| 254 | +meson install -C build | |
| 255 | +``` | |
| 256 | + | |
| 257 | +--- | |
| 258 | + | |
| 259 | +## Development Tools | |
| 260 | + | |
| 261 | +### Compiler | |
| 262 | + | |
| 263 | +**gfortran 9.0+** (GCC Fortran compiler) | |
| 264 | + | |
| 265 | +**Why gfortran?** | |
| 266 | +- Free and open source | |
| 267 | +- Excellent Fortran 2008 support | |
| 268 | +- C interoperability works well | |
| 269 | +- Available on all platforms | |
| 270 | + | |
| 271 | +**Compiler Flags:** | |
| 272 | +```bash | |
| 273 | +# Debug build | |
| 274 | +-g -Wall -Wextra -fcheck=all -fbacktrace | |
| 275 | + | |
| 276 | +# Release build | |
| 277 | +-O3 -march=native -flto | |
| 278 | +``` | |
| 279 | + | |
| 280 | +**Alternative:** Intel Fortran (ifx) also works but not required | |
| 281 | + | |
| 282 | +### Debugger | |
| 283 | + | |
| 284 | +**GDB** (GNU Debugger) | |
| 285 | +```bash | |
| 286 | +gdb ./build/sniffly | |
| 287 | +``` | |
| 288 | + | |
| 289 | +**LLDB** (macOS default) | |
| 290 | +```bash | |
| 291 | +lldb ./build/sniffly | |
| 292 | +``` | |
| 293 | + | |
| 294 | +### Profiling | |
| 295 | + | |
| 296 | +**Linux: perf** | |
| 297 | +```bash | |
| 298 | +perf record ./sniffly /large/directory | |
| 299 | +perf report | |
| 300 | +``` | |
| 301 | + | |
| 302 | +**macOS: Instruments** | |
| 303 | +```bash | |
| 304 | +instruments -t "Time Profiler" ./sniffly /large/directory | |
| 305 | +``` | |
| 306 | + | |
| 307 | +**gprof** (cross-platform) | |
| 308 | +```bash | |
| 309 | +# Compile with -pg flag | |
| 310 | +gfortran -pg -o sniffly ... | |
| 311 | +./sniffly /large/directory | |
| 312 | +gprof sniffly gmon.out | |
| 313 | +``` | |
| 314 | + | |
| 315 | +### Memory Analysis | |
| 316 | + | |
| 317 | +**valgrind** (Linux) | |
| 318 | +```bash | |
| 319 | +valgrind --leak-check=full ./sniffly /tmp | |
| 320 | +``` | |
| 321 | + | |
| 322 | +**AddressSanitizer** (both platforms) | |
| 323 | +```bash | |
| 324 | +gfortran -fsanitize=address -o sniffly ... | |
| 325 | +./sniffly /tmp | |
| 326 | +``` | |
| 327 | + | |
| 328 | +--- | |
| 329 | + | |
| 330 | +## Dependency Installation | |
| 331 | + | |
| 332 | +### macOS (Homebrew) | |
| 333 | + | |
| 334 | +```bash | |
| 335 | +# Install GTK4 and dependencies | |
| 336 | +brew install gtk4 cairo pango glib | |
| 337 | + | |
| 338 | +# Install gtk-fortran (build from source) | |
| 339 | +git clone https://github.com/vmagnin/gtk-fortran.git | |
| 340 | +cd gtk-fortran | |
| 341 | +cmake -B build | |
| 342 | +cmake --build build | |
| 343 | +sudo cmake --install build | |
| 344 | + | |
| 345 | +# Install build tools | |
| 346 | +brew install gfortran meson ninja | |
| 347 | +``` | |
| 348 | + | |
| 349 | +### Ubuntu/Debian | |
| 350 | + | |
| 351 | +```bash | |
| 352 | +# Install GTK4 and dependencies | |
| 353 | +sudo apt install libgtk-4-dev libcairo2-dev libpango1.0-dev libglib2.0-dev | |
| 354 | + | |
| 355 | +# Install gtk-fortran (build from source) | |
| 356 | +git clone https://github.com/vmagnin/gtk-fortran.git | |
| 357 | +cd gtk-fortran | |
| 358 | +cmake -B build | |
| 359 | +cmake --build build | |
| 360 | +sudo cmake --install build | |
| 361 | + | |
| 362 | +# Install build tools | |
| 363 | +sudo apt install gfortran meson ninja-build | |
| 364 | +``` | |
| 365 | + | |
| 366 | +### Arch Linux | |
| 367 | + | |
| 368 | +```bash | |
| 369 | +# Install GTK4 and dependencies | |
| 370 | +sudo pacman -S gtk4 cairo pango glib2 | |
| 371 | + | |
| 372 | +# gtk-fortran from AUR | |
| 373 | +yay -S gtk-fortran | |
| 374 | + | |
| 375 | +# Install build tools | |
| 376 | +sudo pacman -S gcc-fortran meson ninja | |
| 377 | +``` | |
| 378 | + | |
| 379 | +### Fedora/RHEL | |
| 380 | + | |
| 381 | +```bash | |
| 382 | +# Install GTK4 and dependencies | |
| 383 | +sudo dnf install gtk4-devel cairo-devel pango-devel glib2-devel | |
| 384 | + | |
| 385 | +# Install gtk-fortran (build from source) | |
| 386 | +git clone https://github.com/vmagnin/gtk-fortran.git | |
| 387 | +cd gtk-fortran | |
| 388 | +cmake -B build | |
| 389 | +cmake --build build | |
| 390 | +sudo cmake --install build | |
| 391 | + | |
| 392 | +# Install build tools | |
| 393 | +sudo dnf install gcc-gfortran meson ninja-build | |
| 394 | +``` | |
| 395 | + | |
| 396 | +--- | |
| 397 | + | |
| 398 | +## GTK4 Inspector | |
| 399 | + | |
| 400 | +**What is it?** | |
| 401 | +- Built-in GUI debugger for GTK apps | |
| 402 | +- Inspect widget hierarchy | |
| 403 | +- View CSS styles | |
| 404 | +- Monitor signals and events | |
| 405 | +- Performance profiling | |
| 406 | + | |
| 407 | +**How to Enable:** | |
| 408 | +```bash | |
| 409 | +# Set environment variable | |
| 410 | +export GTK_DEBUG=interactive | |
| 411 | + | |
| 412 | +# Run app (inspector opens automatically) | |
| 413 | +./sniffly | |
| 414 | +``` | |
| 415 | + | |
| 416 | +**Features:** | |
| 417 | +- Widget tree browser | |
| 418 | +- Property editor (live changes) | |
| 419 | +- CSS inspector | |
| 420 | +- Signal log | |
| 421 | +- Performance metrics | |
| 422 | + | |
| 423 | +--- | |
| 424 | + | |
| 425 | +## Testing Infrastructure | |
| 426 | + | |
| 427 | +### Unit Testing: FUnit | |
| 428 | + | |
| 429 | +**Why FUnit?** | |
| 430 | +- Pure Fortran testing framework | |
| 431 | +- Simple assertion API | |
| 432 | +- Integrates with Meson | |
| 433 | + | |
| 434 | +**Sample Test:** | |
| 435 | +```fortran | |
| 436 | +module test_squarified | |
| 437 | + use squarified | |
| 438 | + use funit | |
| 439 | + implicit none | |
| 440 | +contains | |
| 441 | + | |
| 442 | + @test | |
| 443 | + subroutine test_aspect_ratio() | |
| 444 | + real :: ratio | |
| 445 | + | |
| 446 | + ratio = aspect_ratio(10, 10) | |
| 447 | + @assertEqual(1.0, ratio, tolerance=0.01) | |
| 448 | + | |
| 449 | + ratio = aspect_ratio(20, 10) | |
| 450 | + @assertEqual(2.0, ratio, tolerance=0.01) | |
| 451 | + end subroutine | |
| 452 | + | |
| 453 | +end module test_squarified | |
| 454 | +``` | |
| 455 | + | |
| 456 | +### Integration Testing: Python | |
| 457 | + | |
| 458 | +**Why Python?** | |
| 459 | +- Easy to script GTK interactions | |
| 460 | +- Screenshot comparison libraries | |
| 461 | +- Rich assertion libraries | |
| 462 | + | |
| 463 | +**Sample Test:** | |
| 464 | +```python | |
| 465 | +import gi | |
| 466 | +gi.require_version('Gtk', '4.0') | |
| 467 | +from gi.repository import Gtk, GLib | |
| 468 | +import subprocess | |
| 469 | +import time | |
| 470 | + | |
| 471 | +def test_launch_and_scan(): | |
| 472 | + # Launch sniffly | |
| 473 | + proc = subprocess.Popen(['./sniffly']) | |
| 474 | + time.sleep(2) | |
| 475 | + | |
| 476 | + # TODO: Automate UI interaction | |
| 477 | + # For now, manual testing with checklist | |
| 478 | + | |
| 479 | + proc.terminate() | |
| 480 | + assert proc.returncode == 0 or proc.returncode is None | |
| 481 | +``` | |
| 482 | + | |
| 483 | +--- | |
| 484 | + | |
| 485 | +## Continuous Integration | |
| 486 | + | |
| 487 | +### GitHub Actions Workflow | |
| 488 | + | |
| 489 | +**.github/workflows/build.yml:** | |
| 490 | +```yaml | |
| 491 | +name: Build and Test | |
| 492 | + | |
| 493 | +on: [push, pull_request] | |
| 494 | + | |
| 495 | +jobs: | |
| 496 | + build-linux: | |
| 497 | + runs-on: ubuntu-latest | |
| 498 | + steps: | |
| 499 | + - uses: actions/checkout@v3 | |
| 500 | + - name: Install dependencies | |
| 501 | + run: | | |
| 502 | + sudo apt update | |
| 503 | + sudo apt install -y libgtk-4-dev gfortran meson ninja-build | |
| 504 | + - name: Build gtk-fortran | |
| 505 | + run: | | |
| 506 | + git clone https://github.com/vmagnin/gtk-fortran.git | |
| 507 | + cd gtk-fortran | |
| 508 | + cmake -B build && cmake --build build | |
| 509 | + sudo cmake --install build | |
| 510 | + - name: Build sniffly | |
| 511 | + run: | | |
| 512 | + meson setup build | |
| 513 | + meson compile -C build | |
| 514 | + - name: Run tests | |
| 515 | + run: meson test -C build | |
| 516 | + | |
| 517 | + build-macos: | |
| 518 | + runs-on: macos-latest | |
| 519 | + steps: | |
| 520 | + - uses: actions/checkout@v3 | |
| 521 | + - name: Install dependencies | |
| 522 | + run: | | |
| 523 | + brew install gtk4 gfortran meson ninja | |
| 524 | + - name: Build gtk-fortran | |
| 525 | + run: | | |
| 526 | + git clone https://github.com/vmagnin/gtk-fortran.git | |
| 527 | + cd gtk-fortran | |
| 528 | + cmake -B build && cmake --build build | |
| 529 | + sudo cmake --install build | |
| 530 | + - name: Build sniffly | |
| 531 | + run: | | |
| 532 | + meson setup build | |
| 533 | + meson compile -C build | |
| 534 | + - name: Run tests | |
| 535 | + run: meson test -C build | |
| 536 | +``` | |
| 537 | + | |
| 538 | +--- | |
| 539 | + | |
| 540 | +## Alternative Stacks Considered | |
| 541 | + | |
| 542 | +### 1. Rust + gtk-rs | |
| 543 | + | |
| 544 | +**Pros:** | |
| 545 | +- Excellent GTK bindings | |
| 546 | +- Memory safety guarantees | |
| 547 | +- Growing ecosystem | |
| 548 | + | |
| 549 | +**Cons:** | |
| 550 | +- Not Fortran (defeats the purpose!) | |
| 551 | +- Would need to rewrite all sniffert logic | |
| 552 | +- Steeper learning curve | |
| 553 | + | |
| 554 | +**Verdict:** ❌ Wrong language | |
| 555 | + | |
| 556 | +### 2. Fortran + QML (Qt Quick) | |
| 557 | + | |
| 558 | +**Pros:** | |
| 559 | +- Modern declarative UI | |
| 560 | +- Hardware-accelerated | |
| 561 | + | |
| 562 | +**Cons:** | |
| 563 | +- No Fortran bindings for QML | |
| 564 | +- Would need C++/Python bridge | |
| 565 | +- Qt is heavy dependency | |
| 566 | + | |
| 567 | +**Verdict:** ❌ No Fortran support | |
| 568 | + | |
| 569 | +### 3. Fortran + Web (Electron-style) | |
| 570 | + | |
| 571 | +**Pros:** | |
| 572 | +- HTML/CSS for UI | |
| 573 | +- Easy prototyping | |
| 574 | + | |
| 575 | +**Cons:** | |
| 576 | +- Huge resource usage | |
| 577 | +- Slow startup | |
| 578 | +- Not native look & feel | |
| 579 | +- Would need REST API layer | |
| 580 | + | |
| 581 | +**Verdict:** ❌ Defeats performance goals | |
| 582 | + | |
| 583 | +### 4. Fortran + Custom OpenGL | |
| 584 | + | |
| 585 | +**Pros:** | |
| 586 | +- Maximum performance | |
| 587 | +- Full control | |
| 588 | + | |
| 589 | +**Cons:** | |
| 590 | +- Reinvent wheel (buttons, text input, etc.) | |
| 591 | +- Months of UI infrastructure work | |
| 592 | +- Platform-specific window management | |
| 593 | + | |
| 594 | +**Verdict:** ❌ Too much work | |
| 595 | + | |
| 596 | +--- | |
| 597 | + | |
| 598 | +## Summary: Why This Stack is Optimal | |
| 599 | + | |
| 600 | +| Requirement | Solution | Rationale | | |
| 601 | +|-------------|----------|-----------| | |
| 602 | +| Pure Fortran | gtk-fortran | Only mature Fortran GUI option | | |
| 603 | +| Cross-platform | GTK4 | Native on Linux, good on macOS | | |
| 604 | +| 2D rendering | Cairo | Built-in, hardware-accelerated | | |
| 605 | +| Text rendering | Pango | Unicode, fonts, ellipsization | | |
| 606 | +| Build system | Meson | GTK standard, dependency handling | | |
| 607 | +| Threading | GLib | Safe GTK integration | | |
| 608 | +| Performance | Native compilation | No interpreter overhead | | |
| 609 | +| Maintainability | Reuse sniffert | Proven algorithms | | |
| 610 | + | |
| 611 | +--- | |
| 612 | + | |
| 613 | +## Risk Mitigation | |
| 614 | + | |
| 615 | +### Risk: gtk-fortran bugs or limitations | |
| 616 | + | |
| 617 | +**Mitigation:** | |
| 618 | +- Join gtk-fortran community early | |
| 619 | +- Contribute fixes upstream | |
| 620 | +- Fallback: C shim layer for problematic functions | |
| 621 | + | |
| 622 | +### Risk: GTK4 not looking native on macOS | |
| 623 | + | |
| 624 | +**Mitigation:** | |
| 625 | +- GTK4 uses Cocoa backend (better than GTK3) | |
| 626 | +- Test early and often on macOS | |
| 627 | +- Accept "good enough" if perfect not achievable | |
| 628 | + | |
| 629 | +### Risk: Performance insufficient | |
| 630 | + | |
| 631 | +**Mitigation:** | |
| 632 | +- Profile early (Week 4) | |
| 633 | +- Viewport culling for large trees | |
| 634 | +- Consider GSK (GTK's GPU backend) if needed | |
| 635 | + | |
| 636 | +### Risk: Build complexity scares users | |
| 637 | + | |
| 638 | +**Mitigation:** | |
| 639 | +- Provide pre-built binaries | |
| 640 | +- Package for all major package managers | |
| 641 | +- Clear installation instructions | |
| 642 | +- CI/CD for automated builds | |
| 643 | + | |
| 644 | +--- | |
| 645 | + | |
| 646 | +## Conclusion | |
| 647 | + | |
| 648 | +This stack provides: | |
| 649 | +1. ✅ **Pure Fortran** (proving the point) | |
| 650 | +2. ✅ **Modern GUI** (GTK4) | |
| 651 | +3. ✅ **Cross-platform** (macOS, Linux) | |
| 652 | +4. ✅ **Performant** (native code) | |
| 653 | +5. ✅ **Maintainable** (reuse sniffert) | |
| 654 | +6. ✅ **Packageable** (standard tools) | |
| 655 | + | |
| 656 | +**We're ready to build!** 🚀 | |
TOOLBAR-TARGETS.mdadded@@ -0,0 +1,256 @@ | ||
| 1 | +# Sniffly Toolbar Feature Targets | |
| 2 | + | |
| 3 | +## Vision | |
| 4 | +Redesign the Sniffly toolbar to match SpaceSniffer's usability while maintaining clean, intuitive design. Focus on most-used operations with clear iconography. | |
| 5 | + | |
| 6 | +--- | |
| 7 | + | |
| 8 | +## Phase 1: Core Toolbar Redesign (PRIORITY) | |
| 9 | + | |
| 10 | +### Remove | |
| 11 | +- **Quit button** - Defer to OS window close button (red X on macOS, X on Windows/Linux) | |
| 12 | + | |
| 13 | +### Add/Reorganize | |
| 14 | + | |
| 15 | +#### Left Section | |
| 16 | +1. **Open Directory Button** (Filing Cabinet Icon) | |
| 17 | + - Opens OS native file picker (GtkFileChooserDialog) | |
| 18 | + - Allows user to select directory to scan | |
| 19 | + - Keyboard shortcut: `Cmd+O` / `Ctrl+O` | |
| 20 | + | |
| 21 | +2. **Path Display** (Read-only text entry or label) | |
| 22 | + - Shows current scan root path | |
| 23 | + - Click filing cabinet icon next to it to trigger Open Directory | |
| 24 | + - Should support text selection for copy/paste | |
| 25 | + | |
| 26 | +3. **Scan Button** (Magnifying Glass Icon) | |
| 27 | + - Positioned immediately after path display | |
| 28 | + - Triggers rescan of current directory | |
| 29 | + - Keyboard shortcut: `F5` or `Cmd+R` | |
| 30 | + | |
| 31 | +4. **Progress Bar** | |
| 32 | + - Keep current implementation | |
| 33 | + - Expands to fill remaining space | |
| 34 | + - Shows scan progress with percentage | |
| 35 | + | |
| 36 | +#### Right Section (floated right) | |
| 37 | +5. **Open in Finder/Explorer Button** (File with Arrow Icon) | |
| 38 | + - Opens currently selected item in OS file browser | |
| 39 | + - macOS: `open -R <path>` (reveals in Finder) | |
| 40 | + - Linux: `xdg-open <path>` or `nautilus <path>` | |
| 41 | + - Windows: `explorer /select,<path>` | |
| 42 | + - Disabled when no selection | |
| 43 | + - Keyboard shortcut: `Cmd+Shift+R` / `Ctrl+Shift+R` | |
| 44 | + | |
| 45 | +6. **Delete Button** (Red Trash/X Icon) | |
| 46 | + - Deletes currently selected item | |
| 47 | + - Priority: System trash → `rm` command as fallback | |
| 48 | + - Shows confirmation dialog | |
| 49 | + - Disabled when no selection | |
| 50 | + - Keyboard shortcut: `d` or `Delete` | |
| 51 | + - Right-click context menu also triggers this | |
| 52 | + | |
| 53 | +--- | |
| 54 | + | |
| 55 | +## Phase 2: Enhanced File Operations | |
| 56 | + | |
| 57 | +### Additional Toolbar Buttons | |
| 58 | +7. **Copy Path Button** (Clipboard Icon) | |
| 59 | + - Copies full path of selected item to clipboard | |
| 60 | + - Keyboard shortcut: `Cmd+Shift+C` / `Ctrl+Shift+C` | |
| 61 | + | |
| 62 | +8. **Properties/Details Button** (Info Icon) | |
| 63 | + - Opens side panel or dialog with file/folder details: | |
| 64 | + - Full path | |
| 65 | + - Size (human-readable + bytes) | |
| 66 | + - Item count (for directories) | |
| 67 | + - Created/modified dates | |
| 68 | + - Permissions | |
| 69 | + - File type | |
| 70 | + - Keyboard shortcut: `i` or `Cmd+I` | |
| 71 | + | |
| 72 | +9. **Refresh Button** (Circular Arrow Icon) | |
| 73 | + - Forces rescan without cache | |
| 74 | + - Clears directory cache before scanning | |
| 75 | + - Keyboard shortcut: `Cmd+Shift+R` / `Ctrl+Shift+R` | |
| 76 | + | |
| 77 | +--- | |
| 78 | + | |
| 79 | +## Phase 3: Search & Filter Features | |
| 80 | + | |
| 81 | +### Filter Toolbar (Second Row, Collapsible) | |
| 82 | +10. **Search/Filter Entry** | |
| 83 | + - Filter by filename pattern (wildcards: `*.txt`, `*cache*`) | |
| 84 | + - Live filtering as you type | |
| 85 | + - Keyboard shortcut: `Cmd+F` / `Ctrl+F` to focus | |
| 86 | + | |
| 87 | +11. **File Type Filter Dropdown** | |
| 88 | + - Quick filters: All, Documents, Images, Videos, Audio, Archives, Code | |
| 89 | + - Based on file extensions | |
| 90 | + - Multiple selection support | |
| 91 | + | |
| 92 | +12. **Size Filter Range** | |
| 93 | + - Min/max size sliders or entry fields | |
| 94 | + - Quick presets: "Large files (>100MB)", "Tiny files (<1MB)" | |
| 95 | + | |
| 96 | +13. **Date Filter** | |
| 97 | + - Filter by modified date: Last day, week, month, year, custom range | |
| 98 | + - "Old files" (not modified in 1+ years) preset | |
| 99 | + | |
| 100 | +14. **Show Hidden Files Toggle** | |
| 101 | + - Toggle visibility of hidden files/folders (starting with `.`) | |
| 102 | + - Keyboard shortcut: `Cmd+Shift+H` | |
| 103 | + | |
| 104 | +--- | |
| 105 | + | |
| 106 | +## Phase 4: Navigation & History | |
| 107 | + | |
| 108 | +15. **Back/Forward Buttons** (Arrow Icons) | |
| 109 | + - Navigate through directory history | |
| 110 | + - Like a web browser | |
| 111 | + - Keyboard shortcuts: `Cmd+[` / `Cmd+]` or `Alt+Left` / `Alt+Right` | |
| 112 | + | |
| 113 | +16. **Up to Parent Button** (Up Arrow Icon) | |
| 114 | + - Navigate to parent directory | |
| 115 | + - Alternative to Backspace key | |
| 116 | + - Shows parent path in tooltip | |
| 117 | + | |
| 118 | +17. **Breadcrumb Navigation** (Already implemented!) | |
| 119 | + - Enhance: Make each path segment clickable | |
| 120 | + - Click any segment to jump to that level | |
| 121 | + | |
| 122 | +--- | |
| 123 | + | |
| 124 | +## Phase 5: Advanced Features (SpaceSniffer Parity) | |
| 125 | + | |
| 126 | +### View Options | |
| 127 | +18. **Toggle File Extensions** | |
| 128 | + - Show/hide file extensions in labels | |
| 129 | + - Keyboard shortcut: `e` | |
| 130 | + | |
| 131 | +19. **Age-based Coloring** | |
| 132 | + - Color files by modification date: | |
| 133 | + - Blue = Recent (< 1 month) | |
| 134 | + - Yellow = Medium (1-12 months) | |
| 135 | + - Red = Old (> 1 year) | |
| 136 | + - Toggle on/off | |
| 137 | + | |
| 138 | +20. **Size Display Mode** | |
| 139 | + - Toggle between actual size vs. allocated size (disk usage) | |
| 140 | + - Useful for showing real disk space usage | |
| 141 | + | |
| 142 | +### Export & Reporting | |
| 143 | +21. **Export to CSV/JSON** | |
| 144 | + - Export directory tree with sizes | |
| 145 | + - Useful for archiving or analysis | |
| 146 | + | |
| 147 | +22. **Copy File List** | |
| 148 | + - Copy list of all files matching current filter | |
| 149 | + - Plain text format, one path per line | |
| 150 | + | |
| 151 | +### Multi-Selection | |
| 152 | +23. **Multiple Selection Support** | |
| 153 | + - `Cmd+Click` / `Ctrl+Click` to select multiple items | |
| 154 | + - `Shift+Click` to select range | |
| 155 | + - Show total size of selection in status bar | |
| 156 | + - Bulk delete support | |
| 157 | + | |
| 158 | +24. **Select All Matching Pattern** | |
| 159 | + - Select all items matching a wildcard pattern | |
| 160 | + - Example: "Select all *.log files" | |
| 161 | + | |
| 162 | +### Comparison Mode | |
| 163 | +25. **Compare Two Scans** | |
| 164 | + - Scan directory, then scan again later | |
| 165 | + - Highlight differences (new, deleted, grown, shrunk files) | |
| 166 | + - Useful for tracking disk usage changes over time | |
| 167 | + | |
| 168 | +--- | |
| 169 | + | |
| 170 | +## Implementation Notes | |
| 171 | + | |
| 172 | +### Icon Sources | |
| 173 | +- Use GTK stock icons where possible (`gtk_image_new_from_icon_name`) | |
| 174 | +- Common icon names: | |
| 175 | + - `folder-open` - Open directory | |
| 176 | + - `view-refresh` - Rescan/refresh | |
| 177 | + - `edit-find` - Search | |
| 178 | + - `edit-delete` - Delete | |
| 179 | + - `edit-copy` - Copy path | |
| 180 | + - `document-properties` - Properties/info | |
| 181 | + - `go-up` - Navigate up | |
| 182 | + - `go-previous` / `go-next` - Back/forward | |
| 183 | + - `document-open` - Open in Finder | |
| 184 | + | |
| 185 | +### GTK Components Needed | |
| 186 | +- `GtkFileChooserDialog` - For directory picker | |
| 187 | +- `GtkPopover` or `GtkDialog` - For properties panel | |
| 188 | +- `GtkEntry` - For search/filter input | |
| 189 | +- `GtkComboBox` - For filter dropdowns | |
| 190 | +- `GtkSwitch` - For toggles (hidden files, etc.) | |
| 191 | +- `GtkClipboard` - For copy path functionality | |
| 192 | + | |
| 193 | +### Platform-Specific Code | |
| 194 | +- **Open in Finder/Explorer**: Use system commands via `iso_c_binding` | |
| 195 | +- **Delete to Trash**: | |
| 196 | + - macOS: `osascript -e 'tell app "Finder" to delete POSIX file "..."'` | |
| 197 | + - Linux: `gio trash <path>` or `trash-put <path>` | |
| 198 | + - Fallback: `rm -rf` with confirmation | |
| 199 | +- **Copy to Clipboard**: Use GTK clipboard API | |
| 200 | + | |
| 201 | +### Keyboard Shortcuts Summary | |
| 202 | +| Shortcut | Action | | |
| 203 | +|----------|--------| | |
| 204 | +| `Cmd+O` / `Ctrl+O` | Open directory picker | | |
| 205 | +| `F5` / `Cmd+R` | Rescan current directory | | |
| 206 | +| `Cmd+Shift+R` | Open in Finder/Explorer | | |
| 207 | +| `d` / `Delete` | Delete selected item | | |
| 208 | +| `Cmd+Shift+C` | Copy path to clipboard | | |
| 209 | +| `i` / `Cmd+I` | Show properties | | |
| 210 | +| `Cmd+F` | Focus search/filter | | |
| 211 | +| `Cmd+Shift+H` | Toggle hidden files | | |
| 212 | +| `Cmd+[` / `Cmd+]` | Back/forward in history | | |
| 213 | +| `Backspace` | Navigate to parent | | |
| 214 | +| `q` | Quit (already implemented) | | |
| 215 | +| `Space` | Select item (already implemented) | | |
| 216 | +| `Enter` | Navigate into item (already implemented) | | |
| 217 | +| `Arrow keys` | Navigate treemap (already implemented) | | |
| 218 | + | |
| 219 | +--- | |
| 220 | + | |
| 221 | +## Current Status (Updated 2025-11-05) | |
| 222 | +- ✅ Basic toolbar with Scan button | |
| 223 | +- ✅ Progress bar (moved to toolbar) | |
| 224 | +- ✅ Breadcrumb display (path only, not clickable yet) | |
| 225 | +- ✅ Keyboard navigation (arrows, enter, backspace, space) | |
| 226 | +- ✅ Mouse hover and selection | |
| 227 | +- ✅ Directory caching for fast re-scans | |
| 228 | +- ✅ **Phase 1 Complete**: Open Directory, Path Display, Scan, Open in Finder, Copy Path, Delete buttons | |
| 229 | +- ✅ **Tooltips**: Hover shows filename, size, item count | |
| 230 | +- ✅ **Status Bar Stats**: Shows "N items (M files) - X.XX GB" | |
| 231 | +- ✅ **Color by File Type**: Images, videos, audio, documents, code, archives colored | |
| 232 | +- ✅ **Size Labels**: Shows formatted sizes on rectangles (when space permits) | |
| 233 | +- ✅ **Copy Path to Clipboard**: Toolbar button + Cmd+Shift+C shortcut | |
| 234 | +- ✅ **Cushioned Treemap**: Van Wijk algorithm for 3D shading effect | |
| 235 | + | |
| 236 | +## Next Steps | |
| 237 | +1. ✅ ~~Implement Phase 1 core toolbar redesign~~ **DONE** | |
| 238 | +2. ✅ ~~Add GtkFileChooserDialog for directory picker~~ **DONE** | |
| 239 | +3. ✅ ~~Implement "Open in Finder" functionality~~ **DONE** | |
| 240 | +4. ✅ ~~Add delete functionality with system trash support~~ **DONE** | |
| 241 | +5. Enhance breadcrumb bar to be clickable (navigate to parent levels) | |
| 242 | +6. Add keyboard shortcut handler for Copy Path (Cmd+Shift+C) | |
| 243 | +7. Implement context menu (right-click) | |
| 244 | +8. Add Properties/Info dialog | |
| 245 | +9. Implement Back/Forward navigation history | |
| 246 | + | |
| 247 | +--- | |
| 248 | + | |
| 249 | +## Design Decisions (CONFIRMED) | |
| 250 | +- ✅ **Drag-and-drop**: YES - Drag directory onto app window to initiate scan | |
| 251 | +- ✅ **Filter toolbar**: Always visible (we have plenty of header space) | |
| 252 | +- ✅ **Context menu**: YES - Right-click context menu on selections | |
| 253 | +- ✅ **Undo for delete**: YES - Implement Cmd/Ctrl+Z undo for delete operations | |
| 254 | +- ✅ **Settings dialog**: Defer to later phases | |
| 255 | + | |
| 256 | +## Priority: ALL FEATURES - Full SpaceSniffer parity! | |
fac_debug.txtadded@@ -0,0 +1,20 @@ | ||
| 1 | +=== FINAL TREE STRUCTURE === | |
| 2 | +. is_file=F has_next_sib=F | |
| 3 | +app is_file=F has_next_sib=T | |
| 4 | +main.f90 is_file=T has_next_sib=F | |
| 5 | +src is_file=F has_next_sib=T | |
| 6 | +core is_file=F has_next_sib=T | |
| 7 | +dir_helpers.c is_file=T has_next_sib=T | |
| 8 | +file_system.f90 is_file=T has_next_sib=T | |
| 9 | +types.f90 is_file=T has_next_sib=F | |
| 10 | +gui is_file=F has_next_sib=F | |
| 11 | +gtk_app.f90 is_file=T has_next_sib=F | |
| 12 | +.gitignore is_file=T has_next_sib=T | |
| 13 | +GETTING_STARTED.md is_file=T has_next_sib=T | |
| 14 | +LICENSE is_file=T has_next_sib=T | |
| 15 | +PROJECT_SUMMARY.md is_file=T has_next_sib=T | |
| 16 | +QUICKSTART.md is_file=T has_next_sib=T | |
| 17 | +README.md is_file=T has_next_sib=T | |
| 18 | +SNIFFLY_MASTER_PLAN.md is_file=T has_next_sib=T | |
| 19 | +TECHNICAL_STACK.md is_file=T has_next_sib=T | |
| 20 | +meson.build is_file=T has_next_sib=F | |