fortrangoingonforty/sniffly / 861b8c1

Browse files

fix pango_layout_get_pixel_size args

Authored by espadonne
SHA
861b8c12898fe697e4fd269b316ac8a046282436
Parents
6a5d00a
Tree
40c65c9

57 changed files

StatusFile+-
A for the breadcrumb 2 0
A BETTER-PROPERTIES-BUTTON.md 13 0
A BUGFIX_SESSION.md 64 0
A Casks/sniffly.rb 16 0
A DYNAMIC_RENDERING_PLAN.md 290 0
A INSTALL.md 97 0
A MACOS_PACKAGING_WALKTHROUGH.md 357 0
A PACKAGING.md 440 0
A PROJECT_SUMMARY.md 481 0
A SNIFFLY-TABS.md 22 0
A SNIFFLY_MASTER_PLAN.md 1101 0
A SPACESNIFFER_FEATURES.md 135 0
A Sniffly.app/Contents/Info.plist 32 0
A Sniffly.app/Contents/MacOS/sniffly-bin bin
A Sniffly.app/Contents/PkgInfo 1 0
A Sniffly.app/Contents/Resources/share/glib-2.0/schemas/gschema.dtd 75 0
A Sniffly.app/Contents/Resources/share/glib-2.0/schemas/gschemas.compiled bin
A Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.Demo4.gschema.xml 26 0
A Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Inspector.gschema.xml 40 0
A Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.ColorChooser.gschema.xml 26 0
A Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.Debug.gschema.xml 25 0
A Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.EmojiChooser.gschema.xml 17 0
A Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.FileChooser.gschema.xml 186 0
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/bn.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/da.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/de.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/es.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/et.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/fi.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/fr.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/hi.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/hu.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/it.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/ja.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/ko.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/lt.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/ms.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/nb.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/nl.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/pl.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/pt.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/ru.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/sv.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/th.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/uk.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/zh.gresource bin
A Sniffly.app/Contents/Resources/share/gtk-4.0/gtk4builder.rng 512 0
A Sniffly.app/Contents/Resources/share/gtk-4.0/valgrind/gtk.supp 355 0
A Sniffly.app/Contents/_CodeSignature/CodeDirectory bin
A Sniffly.app/Contents/_CodeSignature/CodeRequirements bin
A Sniffly.app/Contents/_CodeSignature/CodeRequirements-1 bin
A Sniffly.app/Contents/_CodeSignature/CodeResources 999 0
A Sniffly.app/Contents/_CodeSignature/CodeSignature 0 0
A TECHNICAL_STACK.md 656 0
A TOOLBAR-TARGETS.md 256 0
A fac_debug.txt 20 0
M src/gui/breadcrumb_widget.f90 4 3
for the breadcrumbadded
@@ -0,0 +1,2 @@
1
+[trunk fa16041] only use
2
+ 1 file changed, 14 insertions(+), 3 deletions(-)
BETTER-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
1101 lines changed — click to load
@@ -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-binadded
Binary file changed.
Sniffly.app/Contents/PkgInfoadded
@@ -0,0 +1,1 @@
1
+APPLSNIF
Sniffly.app/Contents/Resources/share/glib-2.0/schemas/gschema.dtdadded
@@ -0,0 +1,75 @@
1
+<!ELEMENT schemalist (schema|enum|flags)* >
2
+<!ATTLIST schemalist gettext-domain CDATA #IMPLIED >
3
+
4
+<!ELEMENT schema (key|child|override)* >
5
+<!ATTLIST schema id             CDATA #REQUIRED
6
+                 path           CDATA #IMPLIED
7
+                 gettext-domain CDATA #IMPLIED
8
+                 extends        CDATA #IMPLIED
9
+                 list-of        CDATA #IMPLIED >
10
+
11
+<!-- enumerated and flags types -->
12
+<!-- each value element maps a nick to a numeric value -->
13
+<!ELEMENT enum (value*) >
14
+<!ATTLIST enum id CDATA #REQUIRED >
15
+
16
+<!ELEMENT flags (value*) >
17
+<!ATTLIST flags id CDATA #REQUIRED >
18
+
19
+<!ELEMENT value EMPTY >
20
+<!-- nick must be at least 2 characters long -->
21
+<!-- value must be parsable as a 32-bit integer -->
22
+<!ATTLIST value nick  CDATA #REQUIRED
23
+                value CDATA #REQUIRED >
24
+
25
+<!ELEMENT key (default|summary?|description?|range?|choices?|aliases?)* >
26
+<!-- name can only contain lowercase letters, numbers and '-' -->
27
+<!-- type must be a GVariant type string -->
28
+<!-- enum must be the id of an enum type that has been defined earlier -->
29
+<!-- flags must be the id of a flags type that has been defined earlier -->
30
+<!-- exactly one of type, enum or flags must be given -->
31
+<!ATTLIST key name  CDATA #REQUIRED
32
+              type  CDATA #IMPLIED
33
+              enum  CDATA #IMPLIED
34
+              flags CDATA #IMPLIED >
35
+
36
+<!-- the default value is specified as a serialized GVariant,
37
+     i.e. you have to include the quotes when specifying a string -->
38
+<!ELEMENT default (#PCDATA) >
39
+<!-- the presence of the l10n attribute marks a default value for
40
+     translation, its value is the gettext category to use -->
41
+<!-- if context is present, it specifies msgctxt to use -->
42
+<!ATTLIST default l10n    (messages|time) #IMPLIED
43
+                  context CDATA           #IMPLIED >
44
+
45
+<!ELEMENT summary (#PCDATA) >
46
+<!ELEMENT description (#PCDATA) >
47
+
48
+<!-- range is only allowed for keys with numeric type -->
49
+<!ELEMENT range EMPTY >
50
+<!-- min and max must be parseable as values of the key type and
51
+     min must be less than or equal to max -->
52
+<!ATTLIST range min CDATA #IMPLIED
53
+                max CDATA #IMPLIED >
54
+
55
+<!-- choices is only allowed for keys with string or string array type -->
56
+<!ELEMENT choices (choice+) >
57
+<!-- each choice element specifies one possible value -->
58
+<!ELEMENT choice EMPTY >
59
+<!ATTLIST choice value CDATA #REQUIRED >
60
+
61
+<!-- aliases is only allowed for keys with enumerated type or with choices -->
62
+<!ELEMENT aliases (alias+) >
63
+<!-- each alias element specifies an alias for one of the possible values -->
64
+<!ELEMENT alias EMPTY >
65
+<!ATTLIST alias value  CDATA #REQUIRED
66
+                target CDATA #REQUIRED >
67
+
68
+<!ELEMENT child EMPTY >
69
+<!ATTLIST child name   CDATA #REQUIRED
70
+                schema CDATA #REQUIRED >
71
+
72
+<!ELEMENT override (#PCDATA) >
73
+<!ATTLIST override name    CDATA #REQUIRED
74
+                   l10n    CDATA #IMPLIED
75
+                   context CDATA #IMPLIED >
Sniffly.app/Contents/Resources/share/glib-2.0/schemas/gschemas.compiledadded
Binary file changed.
Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.Demo4.gschema.xmladded
@@ -0,0 +1,26 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+
3
+<schemalist>
4
+
5
+  <enum id='org.gtk.Demo4.Color'>
6
+    <value nick='red'   value='0'/>
7
+    <value nick='green' value='1'/>
8
+    <value nick='blue'  value='2'/>
9
+  </enum>
10
+
11
+  <schema id='org.gtk.Demo4' path='/org/gtk/Demo4/'>
12
+    <key name='color' enum='org.gtk.Demo4.Color'>
13
+      <default>'red'</default>
14
+    </key>
15
+    <key name='window-size' type='(ii)'>
16
+      <default>(-1, -1)</default>
17
+    </key>
18
+    <key name='maximized' type='b'>
19
+      <default>false</default>
20
+    </key>
21
+    <key name='fullscreen' type='b'>
22
+      <default>false</default>
23
+    </key>
24
+  </schema>
25
+
26
+</schemalist>
Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Inspector.gschema.xmladded
@@ -0,0 +1,40 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<schemalist>
3
+
4
+  <schema id='org.gtk.gtk4.Inspector.Recorder' path='/org/gtk/gtk4/inspector/recorder/'>
5
+    <key name='debug-nodes' type='b'>
6
+      <default>false</default>
7
+      <summary>Insert debug nodes</summary>
8
+      <description>
9
+        If this setting is true, the recorder will insert debug nodes
10
+        into the recording.
11
+      </description>
12
+    </key>
13
+    <key name='record-events' type='b'>
14
+      <default>false</default>
15
+      <summary>Record events</summary>
16
+      <description>
17
+        If this setting is true, the recorder will include events
18
+        in the recording.
19
+      </description>
20
+    </key>
21
+    <key name='highlight-sequences' type='b'>
22
+      <default>false</default>
23
+      <summary>Highlight sequences</summary>
24
+      <description>
25
+        If this setting is true, the recorder will highlight events
26
+        that are part of an event sequence.
27
+      </description>
28
+    </key>
29
+    <key name='dark' type='b'>
30
+      <default>false</default>
31
+      <summary>Set the recorder to dark</summary>
32
+      <description>
33
+        If this setting is true, the recorder will display render nodes
34
+        on a dark background.
35
+      </description>
36
+    </key>
37
+  </schema>
38
+
39
+</schemalist>
40
+
Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.ColorChooser.gschema.xmladded
@@ -0,0 +1,26 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<schemalist>
3
+
4
+  <schema id='org.gtk.gtk4.Settings.ColorChooser' path='/org/gtk/gtk4/settings/color-chooser/'>
5
+    <key name='custom-colors' type='a(dddd)'>
6
+      <default>[]</default>
7
+      <summary>Custom colors</summary>
8
+      <description>
9
+        An array of custom colors to show in the color chooser. Each color is
10
+        specified as a tuple of four doubles, specifying RGBA values between
11
+        0 and 1.
12
+      </description>
13
+    </key>
14
+    <key name='selected-color' type='(bdddd)'>
15
+      <default>(false,1.0,1.0,1.0,1.0)</default>
16
+      <summary>The selected color</summary>
17
+      <description>
18
+         The selected color, described as a tuple whose first member is a
19
+         boolean that is true if a color was selected, and the remaining
20
+         four members are four doubles, specifying RGBA values between
21
+         0 and 1.
22
+      </description>
23
+    </key>
24
+  </schema>
25
+
26
+</schemalist>
Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.Debug.gschema.xmladded
@@ -0,0 +1,25 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<schemalist>
3
+
4
+  <schema id='org.gtk.gtk4.Settings.Debug' path='/org/gtk/gtk4/settings/debug/'>
5
+    <key name='enable-inspector-keybinding' type='b'>
6
+      <default>true</default>
7
+      <summary>Enable inspector keybinding</summary>
8
+      <description>
9
+        If this setting is true, GTK lets the user open an interactive
10
+        debugging window with a keybinding. The default shortcuts for
11
+        the keybinding are Control-Shift-I and Control-Shift-D.
12
+      </description>
13
+    </key>
14
+    <key name='inspector-warning' type='b'>
15
+      <default>true</default>
16
+      <summary>Inspector warning</summary>
17
+      <description>
18
+        If this setting is true, GTK shows a warning before letting
19
+        the user use the interactive debugger.
20
+      </description>
21
+    </key>
22
+  </schema>
23
+
24
+</schemalist>
25
+
Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.EmojiChooser.gschema.xmladded
@@ -0,0 +1,17 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<schemalist>
3
+
4
+  <schema id='org.gtk.gtk4.Settings.EmojiChooser' path='/org/gtk/gtk4/settings/emoji-chooser/'>
5
+    <key name='recently-used-emoji' type='a((aussasasu)u)'>
6
+      <default>[]</default>
7
+      <summary>Recently used Emoji</summary>
8
+      <description>
9
+        An array of Emoji definitions to show in the Emoji chooser. Each Emoji is
10
+        specified as an array of codepoints, name and keywords. The extra
11
+        integer after this pair is the code of the Fitzpatrick modifier to use in
12
+        place of a modifier placeholder (0 or 0x1F3FB) in the codepoint array.
13
+      </description>
14
+    </key>
15
+  </schema>
16
+
17
+</schemalist>
Sniffly.app/Contents/Resources/share/glib-2.0/schemas/org.gtk.gtk4.Settings.FileChooser.gschema.xmladded
@@ -0,0 +1,186 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!--
3
+  Copyright © 2010 Christian Persch
4
+
5
+  This library is free software; you can redistribute it and/or modify
6
+  it under the terms of the GNU Lesser General Public License as published by
7
+  the Free Software Foundation; either version 2.1, or (at your option)
8
+  any later version.
9
+
10
+  This library is distributed in the hope that it will be useful,
11
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
+  GNU Lesser General Public License for more details.
14
+
15
+  You should have received a copy of the GNU Lesser General Public License
16
+  along with this library. If not, see <http://www.gnu.org/licenses/>.
17
+-->
18
+<schemalist>
19
+
20
+  <enum id='org.gtk.gtk4.Settings.FileChooser.LocationMode'>
21
+    <value nick='path-bar' value='0'/>
22
+    <value nick='filename-entry' value='1'/>
23
+  </enum>
24
+
25
+  <enum id='org.gtk.gtk4.Settings.FileChooser.SortColumn'>
26
+    <value nick='name' value='0'/>
27
+    <value nick='size' value='1'/>
28
+    <value nick='type' value='2'/>
29
+    <value nick='modified' value='3'/>
30
+  </enum>
31
+
32
+  <enum id='org.gtk.gtk4.Settings.FileChooser.SortOrder'>
33
+    <value nick='ascending' value='0'/>
34
+    <value nick='descending' value='1'/>
35
+  </enum>
36
+
37
+  <enum id='org.gtk.gtk4.Settings.FileChooser.StartupMode'>
38
+    <value nick='recent' value='0'/>
39
+    <value nick='cwd' value='1'/>
40
+  </enum>
41
+
42
+  <enum id='org.gtk.gtk4.Settings.FileChooser.ClockFormat'>
43
+    <value nick='24h' value='0'/>
44
+    <value nick='12h' value='1'/>
45
+  </enum>
46
+
47
+  <enum id='org.gtk.gtk4.Settings.FileChooser.DateFormat'>
48
+    <value nick='regular' value='0'/>
49
+    <value nick='with-time' value='1'/>
50
+  </enum>
51
+
52
+  <enum id='org.gtk.gtk4.Settings.FileChooser.TypeFormat'>
53
+    <value nick='mime' value='0'/>
54
+    <value nick='description' value='1'/>
55
+    <value nick='category' value='2'/>
56
+  </enum>
57
+
58
+  <enum id='org.gtk.gtk4.Settings.FileChooser.ViewType'>
59
+    <value nick='list' value='0'/>
60
+    <value nick='grid' value='1'/>
61
+  </enum>
62
+
63
+  <schema id='org.gtk.gtk4.Settings.FileChooser' path='/org/gtk/gtk4/settings/file-chooser/'>
64
+    <key name='location-mode' enum='org.gtk.gtk4.Settings.FileChooser.LocationMode'>
65
+      <default>'path-bar'</default>
66
+      <summary>Location mode</summary>
67
+      <description>
68
+	Controls whether the file chooser shows just a path bar, or a visible entry
69
+        for the filename as well, for the benefit of typing-oriented users. The
70
+        possible values for these modes are "path-bar" and "filename-entry".
71
+      </description>
72
+    </key>
73
+    <key name='show-hidden' type='b'>
74
+      <default>false</default>
75
+      <summary>Show hidden files</summary>
76
+      <description>
77
+	Controls whether the file chooser shows hidden files or not.
78
+      </description>
79
+    </key>
80
+    <key type="b" name="sort-directories-first">
81
+      <default>true</default>
82
+      <summary>Show folders first</summary>
83
+      <description>
84
+        If set to true, then folders are shown before files in the list.
85
+      </description>
86
+    </key>
87
+    <key name='expand-folders' type='b'>
88
+      <default>false</default>
89
+      <summary>Expand folders</summary>
90
+      <description>This key is deprecated; do not use it.</description>
91
+    </key>
92
+    <key name='show-size-column' type='b'>
93
+      <default>true</default>
94
+      <summary>Show file sizes</summary>
95
+      <description>
96
+	Controls whether the file chooser shows a column with file sizes.
97
+      </description>
98
+    </key>
99
+    <key name='show-type-column' type='b'>
100
+      <default>true</default>
101
+      <summary>Show file types</summary>
102
+      <description>
103
+	Controls whether the file chooser shows a column with file types.
104
+      </description>
105
+    </key>
106
+    <key name='sort-column' enum='org.gtk.gtk4.Settings.FileChooser.SortColumn'>
107
+      <default>'name'</default>
108
+      <summary>Sort column</summary>
109
+      <description>
110
+	Can be one of "name", "modified", or "size".  It controls
111
+	which of the columns in the file chooser is used for sorting
112
+	the list of files.
113
+      </description>
114
+    </key>
115
+    <key name='sort-order' enum='org.gtk.gtk4.Settings.FileChooser.SortOrder'>
116
+      <default>'ascending'</default>
117
+      <summary>Sort order</summary>
118
+      <description>
119
+	Can be one of the strings "ascending" or "descending".
120
+      </description>
121
+    </key>
122
+    <key name='window-position' type='(ii)'>
123
+      <default>(-1, -1)</default>
124
+      <summary>Window position</summary>
125
+      <description>
126
+        This key is ignored.
127
+      </description>
128
+    </key>
129
+    <key name='window-size' type='(ii)'>
130
+      <default>(-1, -1)</default>
131
+      <summary>Window size</summary>
132
+      <description>
133
+	The size (width, height) of the GtkFileChooserDialog's window, in pixels.
134
+      </description>
135
+    </key>
136
+    <key name='startup-mode' enum='org.gtk.gtk4.Settings.FileChooser.StartupMode'>
137
+      <default>'recent'</default>
138
+      <summary>Startup mode</summary>
139
+      <description>
140
+	Either "recent" or "cwd"; controls whether the file chooser
141
+	starts up showing the list of recently-used files, or the
142
+	contents of the current working directory.
143
+      </description>
144
+    </key>
145
+    <key name='sidebar-width' type='i'>
146
+      <default>-1</default>
147
+      <summary>Sidebar width</summary>
148
+      <description>
149
+	Width in pixels of the file chooser's places sidebar.
150
+      </description>
151
+    </key>
152
+    <key name="clock-format" enum="org.gtk.gtk4.Settings.FileChooser.ClockFormat">
153
+      <default>'24h'</default>
154
+      <summary>Time format</summary>
155
+      <description>
156
+        Whether the time is shown in 24h or 12h format.
157
+      </description>
158
+    </key>
159
+    <key name="date-format" enum="org.gtk.gtk4.Settings.FileChooser.DateFormat">
160
+      <default>'regular'</default>
161
+      <summary>Date format</summary>
162
+      <description>
163
+        The amount of detail to show in the Modified column.
164
+      </description>
165
+    </key>
166
+    <key name="type-format" enum="org.gtk.gtk4.Settings.FileChooser.TypeFormat">
167
+      <default>'category'</default>
168
+      <summary>Type format</summary>
169
+      <description>
170
+        Different ways to show the 'Type' column information.
171
+        Example outputs for a video mp4 file:
172
+        'mime' -> 'video/mp4'
173
+        'description' -> 'MPEG-4 video'
174
+        'category' -> 'Video'
175
+      </description>
176
+    </key>
177
+    <key name="view-type" enum="org.gtk.gtk4.Settings.FileChooser.ViewType">
178
+      <default>'list'</default>
179
+      <summary>View type</summary>
180
+      <description>
181
+        Whether the files are shown in a list or in a grid.
182
+      </description>
183
+    </key>
184
+  </schema>
185
+
186
+</schemalist>
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/bn.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/da.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/de.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/es.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/et.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/fi.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/fr.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/hi.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/hu.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/it.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/ja.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/ko.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/lt.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/ms.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/nb.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/nl.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/pl.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/pt.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/ru.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/sv.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/th.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/uk.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/emoji/zh.gresourceadded
Binary file changed.
Sniffly.app/Contents/Resources/share/gtk-4.0/gtk4builder.rngadded
@@ -0,0 +1,512 @@
1
+<?xml version="1.0"?>
2
+<grammar xmlns="http://relaxng.org/ns/structure/1.0" ns="">
3
+  <start>
4
+    <element name="interface">
5
+      <optional>
6
+        <attribute name="domain">
7
+          <text/>
8
+        </attribute>
9
+      </optional>
10
+      <zeroOrMore>
11
+        <choice>
12
+          <ref name="requires"/>
13
+          <ref name="object"/>
14
+          <ref name="template"/>
15
+          <ref name="menu"/>
16
+        </choice>
17
+      </zeroOrMore>
18
+    </element>
19
+  </start>
20
+  <define name="requires">
21
+    <element name="requires">
22
+      <attribute name="lib">
23
+        <text/>
24
+      </attribute>
25
+      <attribute name="version">
26
+        <text/>
27
+      </attribute>
28
+    </element>
29
+  </define>
30
+  <define name="object">
31
+    <element name="object">
32
+      <optional>
33
+        <attribute name="id">
34
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
35
+        </attribute>
36
+      </optional>
37
+      <attribute name="class">
38
+        <text/>
39
+      </attribute>
40
+      <optional>
41
+        <attribute name="type-func">
42
+          <text/>
43
+        </attribute>
44
+      </optional>
45
+      <optional>
46
+        <attribute name="constructor">
47
+          <text/>
48
+        </attribute>
49
+      </optional>
50
+      <zeroOrMore>
51
+        <choice>
52
+          <ref name="property"/>
53
+          <ref name="signal"/>
54
+          <ref name="child"/>
55
+          <ref name="constraints"/>
56
+          <ref name="ANY"/>
57
+        </choice>
58
+      </zeroOrMore>
59
+    </element>
60
+  </define>
61
+  <define name="template">
62
+    <element name="template">
63
+      <attribute name="class">
64
+        <text/>
65
+      </attribute>
66
+      <attribute name="parent">
67
+        <text/>
68
+      </attribute>
69
+      <zeroOrMore>
70
+        <choice>
71
+          <ref name="property"/>
72
+          <ref name="signal"/>
73
+          <ref name="child"/>
74
+          <ref name="ANY"/>
75
+        </choice>
76
+      </zeroOrMore>
77
+    </element>
78
+  </define>
79
+  <define name="property">
80
+    <element name="property">
81
+      <attribute name="name">
82
+        <text/>
83
+      </attribute>
84
+      <optional>
85
+        <attribute name="translatable">
86
+          <choice>
87
+            <value>yes</value>
88
+            <value>no</value>
89
+          </choice>
90
+        </attribute>
91
+      </optional>
92
+      <optional>
93
+        <attribute name="comments">
94
+          <text/>
95
+        </attribute>
96
+      </optional>
97
+      <optional>
98
+        <attribute name="context">
99
+          <text/>
100
+        </attribute>
101
+      </optional>
102
+      <optional>
103
+        <group>
104
+          <attribute name="bind-source">
105
+            <text/>
106
+          </attribute>
107
+          <optional>
108
+            <attribute name="bind-property">
109
+              <text/>
110
+            </attribute>
111
+          </optional>
112
+          <optional>
113
+            <attribute name="bind-flags">
114
+              <text/>
115
+            </attribute>
116
+          </optional>
117
+        </group>
118
+      </optional>
119
+      <choice>
120
+        <text/>
121
+        <ref name="object"/>
122
+        <ref name="constant"/>
123
+        <ref name="lookup"/>
124
+        <ref name="closure"/>
125
+        <ref name="menu"/>
126
+      </choice>
127
+    </element>
128
+  </define>
129
+  <define name="signal">
130
+    <element name="signal">
131
+      <attribute name="name">
132
+        <text/>
133
+      </attribute>
134
+      <attribute name="handler">
135
+        <text/>
136
+      </attribute>
137
+      <optional>
138
+        <attribute name="after">
139
+          <text/>
140
+        </attribute>
141
+      </optional>
142
+      <optional>
143
+        <attribute name="swapped">
144
+          <text/>
145
+        </attribute>
146
+      </optional>
147
+      <optional>
148
+        <attribute name="object">
149
+          <text/>
150
+        </attribute>
151
+      </optional>
152
+      <optional>
153
+        <attribute name="last_modification_time">
154
+          <text/>
155
+        </attribute>
156
+      </optional>
157
+      <empty/>
158
+    </element>
159
+  </define>
160
+  <define name="child">
161
+    <element name="child">
162
+      <optional>
163
+        <attribute name="type">
164
+          <text/>
165
+        </attribute>
166
+      </optional>
167
+      <optional>
168
+        <attribute name="internal-child">
169
+          <text/>
170
+        </attribute>
171
+      </optional>
172
+      <zeroOrMore>
173
+        <choice>
174
+          <ref name="object"/>
175
+          <ref name="ANY"/>
176
+        </choice>
177
+      </zeroOrMore>
178
+    </element>
179
+  </define>
180
+  <define name="menu">
181
+    <element name="menu">
182
+      <optional>
183
+        <attribute name="id">
184
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
185
+        </attribute>
186
+      </optional>
187
+      <optional>
188
+        <attribute name="domain">
189
+          <text/>
190
+        </attribute>
191
+      </optional>
192
+      <zeroOrMore>
193
+        <choice>
194
+          <ref name="item"/>
195
+          <ref name="submenu"/>
196
+          <ref name="section"/>
197
+        </choice>
198
+      </zeroOrMore>
199
+    </element>
200
+  </define>
201
+  <define name="item">
202
+    <element name="item">
203
+      <optional>
204
+        <attribute name="id">
205
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
206
+        </attribute>
207
+      </optional>
208
+      <zeroOrMore>
209
+        <choice>
210
+          <ref name="attribute_"/>
211
+          <ref name="link"/>
212
+        </choice>
213
+      </zeroOrMore>
214
+    </element>
215
+  </define>
216
+  <define name="attribute_">
217
+    <element name="attribute">
218
+      <attribute name="name">
219
+        <text/>
220
+      </attribute>
221
+      <optional>
222
+        <attribute name="type">
223
+          <text/>
224
+        </attribute>
225
+      </optional>
226
+      <optional>
227
+        <attribute name="translatable">
228
+          <choice>
229
+            <value>yes</value>
230
+            <value>no</value>
231
+          </choice>
232
+        </attribute>
233
+      </optional>
234
+      <optional>
235
+        <attribute name="context">
236
+          <text/>
237
+        </attribute>
238
+      </optional>
239
+      <optional>
240
+        <attribute name="comments">
241
+          <text/>
242
+        </attribute>
243
+      </optional>
244
+      <optional>
245
+        <text/>
246
+      </optional>
247
+    </element>
248
+  </define>
249
+  <define name="link">
250
+    <element name="link">
251
+      <optional>
252
+        <attribute name="id">
253
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
254
+        </attribute>
255
+      </optional>
256
+      <attribute name="name">
257
+        <text/>
258
+      </attribute>
259
+      <zeroOrMore>
260
+        <ref name="item"/>
261
+      </zeroOrMore>
262
+    </element>
263
+  </define>
264
+  <define name="submenu">
265
+    <element name="submenu">
266
+      <optional>
267
+        <attribute name="id">
268
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
269
+        </attribute>
270
+      </optional>
271
+      <zeroOrMore>
272
+        <choice>
273
+          <ref name="attribute_"/>
274
+          <ref name="item"/>
275
+          <ref name="submenu"/>
276
+          <ref name="section"/>
277
+        </choice>
278
+      </zeroOrMore>
279
+    </element>
280
+  </define>
281
+  <define name="section">
282
+    <element name="section">
283
+      <optional>
284
+        <attribute name="id">
285
+          <data type="ID" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"/>
286
+        </attribute>
287
+      </optional>
288
+      <zeroOrMore>
289
+        <choice>
290
+          <ref name="attribute_"/>
291
+          <ref name="item"/>
292
+          <ref name="submenu"/>
293
+          <ref name="section"/>
294
+        </choice>
295
+      </zeroOrMore>
296
+    </element>
297
+  </define>
298
+  <define name="ANY">
299
+    <element>
300
+      <anyName>
301
+        <except>
302
+          <name>interface</name>
303
+          <name>requires</name>
304
+          <name>object</name>
305
+          <name>property</name>
306
+          <name>signal</name>
307
+          <name>child</name>
308
+          <name>menu</name>
309
+          <name>item</name>
310
+          <name>attribute</name>
311
+          <name>link</name>
312
+          <name>submenu</name>
313
+          <name>section</name>
314
+          <name>lookup</name>
315
+          <name>closure</name>
316
+          <name>constant</name>
317
+          <name>constraint</name>
318
+          <name>guide</name>
319
+        </except>
320
+      </anyName>
321
+      <zeroOrMore>
322
+        <attribute>
323
+          <anyName/>
324
+          <text/>
325
+        </attribute>
326
+      </zeroOrMore>
327
+      <interleave>
328
+        <zeroOrMore>
329
+          <ref name="ALL"/>
330
+        </zeroOrMore>
331
+        <optional>
332
+          <text/>
333
+        </optional>
334
+      </interleave>
335
+    </element>
336
+  </define>
337
+  <define name="ALL">
338
+    <element>
339
+      <anyName/>
340
+      <zeroOrMore>
341
+        <attribute>
342
+          <anyName/>
343
+          <text/>
344
+        </attribute>
345
+      </zeroOrMore>
346
+      <interleave>
347
+        <zeroOrMore>
348
+          <ref name="ALL"/>
349
+        </zeroOrMore>
350
+        <optional>
351
+          <text/>
352
+        </optional>
353
+      </interleave>
354
+    </element>
355
+  </define>
356
+  <define name="constant">
357
+    <element name="constant">
358
+      <attribute name="type">
359
+        <text/>
360
+      </attribute>
361
+      <optional>
362
+        <attribute name="translatable">
363
+          <choice>
364
+            <value>yes</value>
365
+            <value>no</value>
366
+          </choice>
367
+        </attribute>
368
+      </optional>
369
+      <optional>
370
+        <attribute name="context">
371
+          <text/>
372
+        </attribute>
373
+      </optional>
374
+      <optional>
375
+        <attribute name="comments">
376
+          <text/>
377
+        </attribute>
378
+      </optional>
379
+      <optional>
380
+        <text/>
381
+      </optional>
382
+    </element>
383
+  </define>
384
+  <define name="lookup">
385
+    <element name="lookup">
386
+      <optional>
387
+        <attribute name="name">
388
+          <text/>
389
+        </attribute>
390
+      </optional>
391
+      <optional>
392
+        <attribute name="type">
393
+          <text/>
394
+        </attribute>
395
+      </optional>
396
+      <choice>
397
+        <text/>
398
+        <ref name="constant"/>
399
+      </choice>
400
+    </element>
401
+  </define>
402
+  <define name="closure">
403
+    <element name="closure">
404
+      <attribute name="type">
405
+        <text/>
406
+      </attribute>
407
+      <attribute name="function">
408
+        <text/>
409
+      </attribute>
410
+      <zeroOrMore>
411
+        <choice>
412
+          <ref name="constant"/>
413
+          <ref name="lookup"/>
414
+        </choice>
415
+      </zeroOrMore>
416
+    </element>
417
+  </define>
418
+  <define name="guide">
419
+    <element name="guide">
420
+      <optional>
421
+        <attribute name="name">
422
+          <text/>
423
+        </attribute>
424
+      </optional>
425
+      <optional>
426
+        <attribute name="min-width">
427
+          <text/>
428
+        </attribute>
429
+      </optional>
430
+      <optional>
431
+        <attribute name="min-height">
432
+          <text/>
433
+        </attribute>
434
+      </optional>
435
+      <optional>
436
+        <attribute name="nat-width">
437
+          <text/>
438
+        </attribute>
439
+      </optional>
440
+      <optional>
441
+        <attribute name="nat-height">
442
+          <text/>
443
+        </attribute>
444
+      </optional>
445
+      <optional>
446
+        <attribute name="max-width">
447
+          <text/>
448
+        </attribute>
449
+      </optional>
450
+      <optional>
451
+        <attribute name="max-height">
452
+          <text/>
453
+        </attribute>
454
+      </optional>
455
+      <optional>
456
+        <attribute name="strength">
457
+          <text/>
458
+        </attribute>
459
+      </optional>
460
+    </element>
461
+  </define>
462
+  <define name="constraint">
463
+    <element name="constraint">
464
+      <attribute name="target">
465
+        <text/>
466
+      </attribute>
467
+      <attribute name="target-attribute">
468
+        <text/>
469
+      </attribute>
470
+      <optional>
471
+        <group>
472
+          <attribute name="source">
473
+            <text/>
474
+          </attribute>
475
+          <attribute name="source-attribute">
476
+            <text/>
477
+          </attribute>
478
+        </group>
479
+      </optional>
480
+      <optional>
481
+        <attribute name="relation">
482
+          <text/>
483
+        </attribute>
484
+      </optional>
485
+      <optional>
486
+        <attribute name="constant">
487
+          <text/>
488
+        </attribute>
489
+      </optional>
490
+      <optional>
491
+        <attribute name="multiplier">
492
+          <text/>
493
+        </attribute>
494
+      </optional>
495
+      <optional>
496
+        <attribute name="strength">
497
+          <text/>
498
+        </attribute>
499
+      </optional>
500
+    </element>
501
+  </define>
502
+  <define name="constraints">
503
+    <element name="constraints">
504
+      <zeroOrMore>
505
+        <choice>
506
+          <ref name="guide"/>
507
+          <ref name="constraint"/>
508
+        </choice>
509
+      </zeroOrMore>
510
+    </element>
511
+  </define>
512
+</grammar>
Sniffly.app/Contents/Resources/share/gtk-4.0/valgrind/gtk.suppadded
@@ -0,0 +1,355 @@
1
+# Actual GTK things
2
+{
3
+  GtkWidgetClass action GPtrArray
4
+  Memcheck:Leak
5
+  fun:malloc
6
+  fun:g_malloc
7
+  fun:g_slice_alloc
8
+  fun:g_ptr_array_sized_new
9
+  fun:g_ptr_array_new
10
+  fun:gtk_widget_class_add_action
11
+}
12
+
13
+{
14
+   GIO modules
15
+   Memcheck:Leak
16
+   match-leak-kinds: definite
17
+   fun:calloc
18
+   ...
19
+   fun:_g_io_module_get_default
20
+}
21
+
22
+{
23
+   GTK media extension gio modules
24
+   Memcheck:Leak
25
+   match-leak-kinds: definite
26
+   fun:calloc
27
+   ...
28
+   fun:g_io_module_new
29
+   ...
30
+   fun:gtk_media_file_extension_init
31
+}
32
+
33
+# AMD driver
34
+{
35
+  radeonsi_dri general
36
+  Memcheck:Leak
37
+  fun:calloc
38
+  ...
39
+  obj:/usr/lib*/dri/radeonsi_dri.so
40
+}
41
+{
42
+  radeonsi_dri general
43
+  Memcheck:Leak
44
+  fun:malloc
45
+  ...
46
+  obj:/usr/lib*/dri/radeonsi_dri.so
47
+}
48
+
49
+# mesa driver stuff
50
+{
51
+   i965 addr4
52
+   Memcheck:Addr4
53
+   obj:/usr/lib*/dri/i965_dri.so*
54
+}
55
+
56
+{
57
+   i965 addr8
58
+   Memcheck:Addr8
59
+   obj:/usr/lib*/dri/i965_dri.so*
60
+}
61
+
62
+{
63
+   i965 memcpy
64
+   Memcheck:Addr8
65
+   fun:memcpy*
66
+   obj:/usr/lib*/dri/i965_dri.so*
67
+}
68
+
69
+{
70
+   i965 memcpy
71
+   Memcheck:Addr2
72
+   fun:memcpy*
73
+   obj:/usr/lib*/dri/i965_dri.so*
74
+}
75
+
76
+{
77
+   mesa memcmp 8
78
+   Memcheck:Addr8
79
+   fun:*memcmp*
80
+   obj:/usr/lib*/dri/i965_dri.so*
81
+}
82
+
83
+{
84
+   mesa memcmp 1
85
+   Memcheck:Addr1
86
+   fun:*memcmp*
87
+   obj:/usr/lib*/dri/i965_dri.so*
88
+}
89
+
90
+{
91
+   mesa memset 8
92
+   Memcheck:Addr8
93
+   fun:*memset*
94
+   obj:/usr/lib*/dri/i965_dri.so
95
+}
96
+
97
+{
98
+   mesa realpath
99
+   Memcheck:Leak
100
+   match-leak-kinds: definite
101
+   fun:malloc
102
+   fun:realpath@@GLIBC_2.3
103
+   obj:*
104
+   obj:*
105
+   obj:*
106
+   obj:*
107
+   obj:*
108
+   obj:*
109
+   obj:*
110
+   obj:*
111
+   fun:epoxy_eglInitialize_global_rewrite_ptr
112
+}
113
+
114
+{
115
+   mesa calloc
116
+   Memcheck:Leak
117
+   match-leak-kinds: definite
118
+   fun:calloc
119
+   obj:*
120
+   obj:*
121
+   obj:*
122
+   obj:*
123
+   obj:*
124
+   obj:*
125
+   obj:*
126
+   obj:*
127
+   obj:*
128
+   fun:epoxy_eglInitialize_global_rewrite_ptr
129
+}
130
+
131
+{
132
+   epoxy strncmp
133
+   Memcheck:Addr8
134
+   fun:strncmp
135
+   ...
136
+   fun:epoxy_eglInitialize_global_rewrite_ptr
137
+}
138
+
139
+{
140
+   mesa malloc
141
+   Memcheck:Leak
142
+   match-leak-kinds: definite
143
+   fun:malloc
144
+   obj:/usr/lib*/dri/i965_dri.so*
145
+}
146
+
147
+{
148
+   mesa glReadPixels
149
+   Memcheck:Addr16
150
+   obj:*
151
+   obj:*
152
+   obj:*
153
+   obj:*
154
+   obj:*
155
+   fun:epoxy_glReadPixels_global_rewrite_ptr
156
+}
157
+
158
+{
159
+   epoxy glxQueryServerString 1
160
+   Memcheck:Leak
161
+   fun:malloc
162
+   fun:XextAddDisplay
163
+   obj:*
164
+   obj:*
165
+   obj:*
166
+   obj:*
167
+   obj:*
168
+   fun:epoxy_glXQueryServerString_global_rewrite_ptr
169
+
170
+}
171
+
172
+{
173
+   epoxy glxQueryServerString 2
174
+   Memcheck:Leak
175
+   match-leak-kinds: definite
176
+   fun:malloc
177
+   fun:realpath*
178
+   obj:*
179
+   obj:*
180
+   obj:*
181
+   obj:*
182
+   obj:*
183
+   obj:*
184
+   obj:*
185
+   fun:epoxy_glXQueryServerString_global_rewrite_ptr
186
+}
187
+
188
+{
189
+   epoxy glGetTexImage
190
+   Memcheck:Addr16
191
+   obj:*
192
+   obj:*
193
+   obj:*
194
+   obj:*
195
+   obj:*
196
+   fun:epoxy_glGetTexImage_global_rewrite_ptr
197
+}
198
+
199
+
200
+
201
+
202
+# Fontconfig
203
+{
204
+   FcFontSetList
205
+   Memcheck:Leak
206
+   match-leak-kinds: definite
207
+   fun:malloc
208
+   obj:/usr/lib*/libfontconfig.so*
209
+   obj:/usr/lib*/libfontconfig.so*
210
+   fun:FcFontSetList
211
+}
212
+
213
+{
214
+   FcPatternObjectInsertElt
215
+   Memcheck:Leak
216
+   match-leak-kinds: definite
217
+   fun:malloc
218
+   fun:FcPatternObjectInsertElt
219
+}
220
+
221
+{
222
+   FcPatternObjectInsertElt2
223
+   Memcheck:Leak
224
+   match-leak-kinds: definite
225
+   fun:realloc
226
+   fun:FcPatternObjectInsertElt
227
+}
228
+
229
+{
230
+   FcFontRenderPrepare
231
+   Memcheck:Leak
232
+   match-leak-kinds: definite
233
+   fun:realloc
234
+   obj:/usr/lib*/libfontconfig.so*
235
+   obj:/usr/lib*/libfontconfig.so*
236
+   fun:FcFontRenderPrepare
237
+}
238
+
239
+{
240
+   FcDefaultSubstitute
241
+   Memcheck:Leak
242
+   match-leak-kinds: definite
243
+   fun:realloc
244
+   obj:/usr/lib*/libfontconfig.so*
245
+   obj:/usr/lib*/libfontconfig.so*
246
+   fun:FcDefaultSubstitute
247
+}
248
+
249
+{
250
+   FcDefaultSubstituteWithPat
251
+   Memcheck:Leak
252
+   match-leak-kinds: definite
253
+   fun:realloc
254
+   obj:/usr/lib*/libfontconfig.so*
255
+   obj:/usr/lib*/libfontconfig.so*
256
+   fun:FcDefaultSubstituteWithPat
257
+}
258
+
259
+{
260
+   FcConfigSubstituteWithPat
261
+   Memcheck:Leak
262
+   match-leak-kinds: definite
263
+   fun:realloc
264
+   obj:/usr/lib*/libfontconfig.so*
265
+   obj:/usr/lib*/libfontconfig.so*
266
+   fun:FcConfigSubstituteWithPat
267
+}
268
+
269
+# Pixman
270
+{
271
+   pixman_image_composite32
272
+   Memcheck:Cond
273
+   obj:/usr/lib*/libpixman-1.so*
274
+   obj:/usr/lib*/libpixman-1.so*
275
+   fun:pixman_image_composite32
276
+}
277
+
278
+# Pango
279
+{
280
+   pango 1
281
+   Memcheck:Leak
282
+   match-leak-kinds: definite
283
+   fun:realloc
284
+   obj:/usr/lib*/libfontconfig.so*
285
+   obj:/usr/lib*/libfontconfig.so*
286
+   obj:/usr/lib*/libcairo.so*
287
+   fun:pango_cairo_fc_font_map_fontset_key_substitute
288
+}
289
+
290
+{
291
+  pango 2
292
+  Memcheck:Leak
293
+  fun:realloc
294
+  obj:/usr/lib*/libfontconfig.so*
295
+  obj:/usr/lib*/libfontconfig.so*
296
+  fun:_cairo_ft_font_options_substitute
297
+}
298
+
299
+# GLib
300
+{
301
+  glib GQuark
302
+  Memcheck:Leak
303
+  match-leak-kinds: definite
304
+  fun:malloc
305
+  ...
306
+  fun:g_quark_*
307
+}
308
+{
309
+  glib GQuark
310
+  Memcheck:Leak
311
+  match-leak-kinds: definite
312
+  fun:malloc
313
+  ...
314
+  fun:g_intern_static_string
315
+}
316
+{
317
+  glib GQuark
318
+  Memcheck:Leak
319
+  match-leak-kinds: definite
320
+  fun:malloc
321
+  ...
322
+  fun:g_intern_string
323
+}
324
+{
325
+  xdg-mime init
326
+  Memcheck:Leak
327
+  match-leak-kinds: definite
328
+  fun:malloc
329
+  ...
330
+  fun:xdg_mime_init*
331
+}
332
+{
333
+  xdg-mime init
334
+  Memcheck:Leak
335
+  match-leak-kinds: definite
336
+  fun:calloc
337
+  ...
338
+  fun:xdg_mime_init*
339
+}
340
+{
341
+  glib init
342
+  Memcheck:Leak
343
+  match-leak-kinds: definite
344
+  fun:malloc
345
+  ...
346
+  fun:glib_init_ctor
347
+}
348
+
349
+# Threads
350
+{
351
+   pthread
352
+   Memcheck:Leak
353
+   fun:calloc
354
+   fun:_dl_allocate_tls
355
+}
Sniffly.app/Contents/_CodeSignature/CodeDirectoryadded
Binary file changed.
Sniffly.app/Contents/_CodeSignature/CodeRequirementsadded
Binary file changed.
Sniffly.app/Contents/_CodeSignature/CodeRequirements-1added
Binary file changed.
Sniffly.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/CodeSignatureadded
TECHNICAL_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
src/gui/breadcrumb_widget.f90modified
@@ -235,7 +235,8 @@ contains
235235
     type(c_ptr), value :: area, cr, user_data
236236
     integer(c_int), value :: width, height
237237
     type(c_ptr) :: layout, font_desc
238
-    integer(c_int) :: text_width, text_height, x_offset
238
+    integer(c_int), target :: text_width, text_height
239
+    integer(c_int) :: x_offset
239240
     integer :: i
240241
     character(len=:), allocatable :: separator
241242
 
@@ -286,7 +287,7 @@ contains
286287
                                   int(len_trim(cached_segment_names(i)), c_int))
287288
 
288289
       ! Get text size
289
-      call pango_layout_get_pixel_size(layout, text_width, text_height)
290
+      call pango_layout_get_pixel_size(layout, c_loc(text_width), c_loc(text_height))
290291
 
291292
       ! Store bounds for hit-testing
292293
       segment_rects(i)%x = x_offset
@@ -312,7 +313,7 @@ contains
312313
 
313314
         call pango_layout_set_text(layout, trim(separator)//c_null_char, &
314315
                                     int(len_trim(separator), c_int))
315
-        call pango_layout_get_pixel_size(layout, text_width, text_height)
316
+        call pango_layout_get_pixel_size(layout, c_loc(text_width), c_loc(text_height))
316317
         call cairo_move_to(cr, real(x_offset, c_double), &
317318
                            real((height - text_height) / 2, c_double))
318319
         call pango_cairo_show_layout(cr, layout)