@@ -5,7 +5,7 @@ How Rusty Commander discovers SMB hosts on the local network.
55## Overview
66
77Network hosts are discovered automatically using ** Bonjour** (Apple's implementation of mDNS/DNS-SD). When an
8- SMB-capable device advertises itself on the local network, it appears in the volume selector under "Network" .
8+ SMB-capable device advertises itself on the local network, it appears in the Network browser view .
99
1010## How Bonjour works
1111
@@ -43,90 +43,103 @@ Delegate receives callbacks as services appear/disappear
4343NSNetService objects (one per discovered host)
4444 │
4545 ▼
46- resolve() each to get IP address + port
46+ hostname/IP derived from service name (lazy resolution)
4747```
4848
4949### Challenges
5050
51- | Challenge | Solution |
52- | ---------------------- | --------------------------------------------------------------------------------------------------------- |
53- | ** Delegate pattern** | Create a Rust struct that implements ` NSNetServiceBrowserDelegate ` using ` objc2 ` 's ` declare_class !` macro |
54- | ** Async/streaming** | Services appear over time; use Rust channels or async streams to propagate updates |
55- | ** Service resolution** | Each ` NSNetService ` needs ` resolve() ` call for IP/hostname (another async operation) |
56- | ** Run loop** | Bonjour callbacks require a run loop; use Tauri's main thread or dedicated run loop |
51+ | Challenge | Solution |
52+ | ---------------------- | -------------------------------------------------------------------------------------------------------- |
53+ | ** Delegate pattern** | Create a Rust struct that implements ` NSNetServiceBrowserDelegate ` using ` objc2 ` 's ` define_class !` macro |
54+ | ** Async/streaming** | Services appear over time; use Tauri events to propagate updates |
55+ | ** Service resolution** | Hostname derived from service name, IP resolved via DNS on hover/demand |
56+ | ** Run loop** | Bonjour callbacks require a run loop; use Tauri's main thread |
5757
5858### Lifecycle
5959
60601 . ** App startup** : Start ` NSNetServiceBrowser ` listening for ` _smb._tcp.local `
61612 . ** Continuous** : Receive callbacks as hosts appear/disappear on network
62- 3 . ** Cache results** : Keep discovered hosts in memory for instant volume selector display
63- 4 . ** Volume selector opened** : Show cached hosts immediately; continue updating as new hosts found
62+ 3 . ** Cache results** : Keep discovered hosts in memory for instant display
63+ 4 . ** Network view opened** : Show cached hosts immediately; continue updating as new hosts found
6464
6565### Lazy resolution
6666
67- Resolution (getting IP/hostname from ` NSNetService ` ) adds ~ 50–200 ms latency per host. To keep discovery snappy :
67+ Resolution (getting IP/hostname) is deferred until needed to keep discovery fast :
6868
6969- ** On discovery** : Store host name only (from mDNS announcement)
70- - ** On hover (500 ms debounce) ** : Resolve that specific host
70+ - ** On hover** : Resolve that specific host's hostname and IP
7171- ** On navigate** : Resolve if not already cached
7272- ** Cache** : Keep resolved addresses in memory
7373
74- This way the volume selector populates instantly with host names, and resolution happens just-in-time.
74+ This way the network browser populates instantly with host names, and resolution happens just-in-time.
7575
7676### Host disappearance
7777
7878Bonjour proactively notifies when hosts stop advertising (via ` netServiceBrowser:didRemoveService: ` ).
7979
8080** UI behavior when a host disappears:**
8181
82- - ** In volume selector** : Remove host from list, keep cursor at same index (or last item if was last)
83- - ** In file pane browsing network hosts** : Remove disappeared host, handle gracefully like any deleted item
84-
85- ### Prefetching for known hosts
86-
87- After discovery settles (~ 3 seconds), prefetch share information for hosts in ` knownNetworkShares ` :
88-
89- ```
90- Discovery complete
91- → For each known host that was discovered:
92- → Queue share enumeration (parallel, cap at 10)
93- → Cache results for instant display when user navigates
94- ```
95-
96- This provides instant response for servers the user has connected to before, without probing unknown hosts.
82+ - ** In Network browser** : Remove host from list, adjust selection
83+ - ** During connection** : Show appropriate error if mid-operation
9784
9885## UX behavior
9986
100- ### In the volume selector
87+ ### Volume selector
88+
89+ The volume selector shows a single "Network" item under the Network section:
10190
10291```
10392📁 Favorites
10493 └── Documents
10594 └── Downloads
10695📁 Macintosh HD
10796📁 External Drive
108- 📶 Network ← Click to expand or show inline
109- └── David's M1 MBP
110- └── Naspolya
111- └── PI
112- └── (Searching...) ← While initial discovery in progress
97+ 🌐 Network ← Click to open Network browser
98+ ```
99+
100+ ### Network browser view
101+
102+ When user clicks "Network" in the volume selector, the pane switches to the Network browser view:
103+
104+ ```
105+ ┌────────────────────────────────────────────────────────────┐
106+ │ Name │ IP address │ Hostname │ Status │
107+ ├────────────────────────────────────────────────────────────┤
108+ │ 🖥️ David's MacBook │ 192.168.1.10 │ macbook.local │ — │
109+ │ 🖥️ NAS-Server │ 192.168.1.50 │ nas.local │ — │
110+ │ 🖥️ Office-PC │ — │ — │ — │
111+ │ │
112+ │ Searching... (if discovery in progress) │
113+ └────────────────────────────────────────────────────────────┘
113114```
114115
115- ### Progress indication
116+ ** Columns:**
117+
118+ - ** Name** : Host's advertised name from Bonjour
119+ - ** IP address** : Resolved IP (on hover/demand), or "—" if not yet resolved
120+ - ** Hostname** : Derived ` .local ` hostname
121+ - ** Shares** : Share count (future, currently "—")
122+ - ** Status** : Connection status (future, currently "—")
123+
124+ ** Keyboard navigation:**
125+
126+ - Arrow Up/Down: Navigate between hosts
127+ - Enter: Select host (future: show shares)
128+ - Backspace: No-op (can't go "up" from network root)
129+
130+ ** Back/Forward navigation:**
116131
117- - ** While searching** : Subtle spinner or "Searching..." text
118- - ** Hosts appear incrementally** : Each host shows up as discovered (streaming UX)
119- - ** After ~ 3–5 seconds** : Consider discovery "complete" but keep listening for changes
132+ - Works as expected: going Back returns to previous file view
133+ - Coming Forward again returns to Network browser
120134
121135## Network change detection
122136
123- Bonjour automatically handles network changes, but we enhance this :
137+ Bonjour automatically handles network changes:
124138
125- | Event | Detection | Action |
126- | ------------------------ | ----------------------- | ---------------------- |
127- | Host starts advertising | Bonjour callback | Add to list |
128- | Host stops advertising | Bonjour callback | Remove from list |
129- | Network interface change | ` SCNetworkReachability ` | Pause/resume discovery |
139+ | Event | Detection | Action |
140+ | ----------------------- | ---------------- | ---------------- |
141+ | Host starts advertising | Bonjour callback | Add to list |
142+ | Host stops advertising | Bonjour callback | Remove from list |
130143
131144## What Bonjour doesn't cover
132145
@@ -156,16 +169,13 @@ Discovery is lightweight enough to start immediately at app launch.
156169
157170## Testing
158171
159- ### Unit tests
172+ ### Backend unit tests
160173
161- - Mock ` NSNetServiceBrowser ` to simulate host discovery
162- - Test callback handling for service found/lost events
163- - Test caching and deduplication logic
164- - Test lazy resolution caching
174+ - ` test_service_name_to_hostname ` : Tests hostname derivation from service names
175+ - ` test_service_name_to_id ` : Tests ID generation from service names
176+ - ` test_network_host_serialization ` : Tests JSON serialization of NetworkHost
165177
166- ### Integration tests
178+ ### Frontend tests
167179
168- - Verify hosts appear in volume list when discovered
169- - Test host disappearance when service goes offline
170- - Test behavior when no hosts found (empty "Network" section)
171- - Test prefetching triggers for known hosts
180+ - ` network-hosts.test.ts ` : Tests network host type interfaces and event handling logic
181+ - Integration tests verify NetworkBrowser renders correctly and handles events
0 commit comments