feat(ui): sidebar nav rewrite — mockup-spec filter rail#14
Merged
Conversation
… / Network / Logs)
The merged redesign kept the original SecVF marketing sidebar (big
hex logo + 26pt "Sec/VF" title + framework subtitle + ▸ Malware
Analysis / Isolated Sandbox / Virtual Networking bullets) and added
a small "LOGS" button group at the bottom. The mockup specced
something fundamentally different: a *functional* navigation rail
that drives the table content.
This PR closes that drift.
Before
------
- 170×120 hex logo (huge — eats top third of the rail)
- 26pt heavy mono "Sec/VF" title
- "Security Virtualization Framework" subtitle
- 3-line marketing bullet list
- LOGS button group (3 entries)
- Framework info pinned to bottom ("Built on Apple Virtualization
Framework / github.com/DaxxSec/SecVF")
After
-----
- Compact brand block: 28pt hex mark + 16pt "SecVF" wordmark + 8pt
"Security Virtualization" tag (~88pt total vs ~225pt before)
- **Filters** section: All VMs / ● Running / ◐ Paused / ○ Stopped
with live count badges, single-select, drives the table
- **Operating System** section: All / 🐧 Linux / ⌘ macOS / ▩ Windows
with live count badges, single-select
- **Network** section: All / ⊘ Isolated / ⇄ Virtual / 🌐 NAT
with live count badges, single-select
- **Logs** section: ◆ Security / ◆ Network / ◆ ISO Cache —
re-implemented in the same TacticalSidebarSection style, dispatches
through the responder chain to AppDelegate's existing handlers,
pops selection after action (logs aren't a persistent filter)
- Framework info dropped from the sidebar — bottom status bar
already carries that information
New component: `TacticalSidebarSection`
---------------------------------------
Reusable view for "section label + selectable rows with leading
glyph and trailing count". Single-select radio within a section;
sections are independent. Hover + selection states match the
tactical palette (OD-green selection band, accent glyph). Click
fires an `onSelect(id)` closure with the row's identifier.
`refreshCounts(_:)` lets the controller push new badge values in
place without rebuilding the layout — cheaper for the common
"VM list changed, counts shifted" path.
Filter composition
------------------
The three sidebar filters AND-compose with each other and with
the existing `runningFilterIDs` set (Focus Running toolbar button):
```
displayedStandardVMs =
master.list
|> ids in runningFilterIDs (if any)
|> status matches sidebarStatusFilter (if any)
|> osType matches sidebarOSFilter (if any)
|> networkConfig matches sidebarNetworkFilter (if any)
```
Pure predicates live as static functions on VMLibraryWindowController
(matchesStatusFilter / matchesOSFilter / matchesNetworkFilter) so
the rules are testable without touching the window.
"Isolated" in the Network section means virtual-mode VMs with no
router relationship at all — not a router, not a guest. That
matches the operator's mental model of "this VM can't talk to
anything outside its own L2".
Tests
-----
New `SidebarFilterTests`: 11 tests covering each predicate's
positive + negative cases, including:
- Paused covers both .starting and .stopping (the two transient
framework states)
- OS matching is case-insensitive substring (handles "Kali Linux",
"Apple Mac OS", etc.)
- Isolated excludes both routers and guests, even in virtual mode
Full suite: 261/261 passing on this branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
You called out lots of drift between the mockup's sidebar and what shipped:
Side-by-side, here's the gap I was closing:
The current sidebar is marketing real estate; the mockup's is the primary table filter UI. This PR closes that.
What's new
TacticalSidebarSection— reusable view: section label + selectable rows with leading glyph and trailing count. Single-select radio within a section; sections are independent. Hover + selection states use the tactical palette. Click firesonSelect(id).Sidebar layout, top → bottom:
The framework-info footer ("Built on Apple Virtualization Framework") is gone from the sidebar — the bottom status bar already shows that info.
Filter composition
The three sidebar filters AND-compose with each other and with the existing `runningFilterIDs` (Focus Running toolbar):
```
displayedStandardVMs =
master.list
|> ids in runningFilterIDs (if any)
|> status matches statusFilter (if any)
|> osType matches osFilter (if any)
|> networkConfig matches networkFilter (if any)
```
Pure predicates live as static funcs on `VMLibraryWindowController` (`matchesStatusFilter` / `matchesOSFilter` / `matchesNetworkFilter`) so the rules are testable without touching the window.
"Isolated" in the Network section means virtual-mode VMs with no router relationship — not a router, not a guest. That matches "this VM can't talk to anything outside its own L2".
Tests
New `SidebarFilterTests` — 11 passing:
Full suite: 261/261 green on this branch (one pre-existing flaky network test skipped as before).
Test plan
Out of scope
🤖 Generated with Claude Code