A native desktop GUI for sbom-utility, built with Fyne (BSD-3-Clause). No JavaScript. No Electron. No browser engine. No GPL or LGPL dependencies.
gui/
├── README.md ← this file
├── FyneApp.toml ← Fyne app metadata (name, ID, version, icon path)
├── main.go ← Fyne app entry point; mirrors main.go init() pattern
├── theme/
│ ├── macos.go ← custom macOS-inspired Fyne theme (global)
│ └── viewer.go ← dark editor theme for the BOM source viewer
├── bridge/
│ ├── doc.go ← package documentation
│ ├── bom_info.go ← parses BOM spec-version for the status bar
│ ├── validate.go ← wraps cmd.Validate()
│ ├── license.go ← wraps cmd.ListLicenses()
│ ├── component.go ← wraps cmd.ListComponents()
│ ├── resource.go ← wraps cmd.ListResources()
│ └── vulnerability.go ← wraps cmd.ListVulnerabilities()
├── screens/
│ ├── doc.go ← package documentation
│ ├── state.go ← shared AppState (BOM file path, BOM info)
│ ├── load.go ← Load BOM file-open dialog + raw viewer
│ ├── view.go ← View tab (raw BOM source viewer)
│ ├── validate.go ← Validate tab
│ ├── license.go ← Licenses tab
│ ├── component.go ← Components tab
│ ├── resource.go ← Resources tab
│ └── vulnerability.go ← Vulnerabilities tab
└── widgets/
├── doc.go ← package documentation
├── filepicker.go ← reusable file-picker row (label + entry + Browse button)
├── results.go ← scrollable monospace results view
├── sidepanel.go ← collapsible ▶/▼ toggle panel
└── statusbar.go ← bottom status bar (spec version + file path)
Separate binary — CLI is untouched.
The GUI is built from ./gui and produces its own binary. The existing CLI binary
(./) is completely unaffected; its main.go, cmd/, and all existing tests are
unchanged.
Bridge layer — no code duplication.
Each bridge/*.go file translates GUI inputs into the exact same function calls the
CLI cobra commands use. It does this by writing into utils.GlobalFlags (the same
global the CLI uses) and then calling the real exported cmd.* function directly —
no exec.Command, no re-implementation of any logic.
FilePicker + flag widgets
│
▼
bridge/validate.go sets utils.GlobalFlags.PersistentFlags.InputFile
bridge/license.go sets utils.GlobalFlags.PersistentFlags.OutputFormat
bridge/component.go sets per-command flag structs (ValidateFlags, etc.)
… calls cmd.Validate() / cmd.ListLicenses() / …
│
▼
cmd/validate.go ← original, unmodified CLI implementation
cmd/license_list.go
cmd/component.go
…
│
▼
bytes.Buffer ──────────────▶ widgets/results.go (scrollable text view)
Automatic feature parity.
Because every screen calls the real cmd.* function, any bug fix or new flag added
to the CLI is automatically available in the GUI without any GUI-side changes.
Async execution.
Every "Run" button fires work in a go func(){} goroutine so the Fyne UI remains
responsive during long scans or large BOM files.
The GUI uses two cooperating themes defined in gui/theme/:
| File | Scope |
|---|---|
macos.go |
Global — applied to the whole application via a.Settings().SetTheme() |
viewer.go |
Local — applied only to the BOM source viewer via container.NewThemeOverride() |
Targets macOS Ventura / Sonoma light-mode aesthetics and is active on all platforms.
| Role | Hex | macOS semantic |
|---|---|---|
| Window background | #F5F5F5 |
Window chrome grey |
| Panel / input background | #FFFFFF |
White content areas |
| Sidebar / header rows | #EBEBEB |
Sidebar grey |
| Primary accent | #007AFF |
System blue |
| Focus ring / selection | #007AFF 20 % |
Translucent accent |
| Primary text | #1D1D1F |
Label |
| Secondary / placeholder text | #6E6E73 |
Secondary label |
| Separator / input border | #D2D2D7 |
Separator |
| Error | #FF3B30 |
System red |
| Warning | #FF9F0A |
System amber |
| Success | #30D158 |
System green |
| Shadow | #000000 15 % |
Drop shadow |
Sizes follow the Apple Human Interface Guidelines:
| Token | Size (pt) |
|---|---|
| Body text | 13 |
| Caption | 11 |
| Heading | 17 |
| Sub-heading | 15 |
| Padding | 6 |
| Inner padding | 4 |
| Input border | 1 |
| Input corner radius | 5 |
| Scroll bar width | 8 |
| Separator thickness | 1 |
Font note: The theme uses Fyne's built-in sans-serif faces (regular, bold, italic, monospace), which are visually close to Apple's SF Pro at these sizes.
The BOM source viewer (the View pane and the raw viewer shown after Load BOM) uses a separate dark-editor colour scheme, applied via container.NewThemeOverride. Only the widget tree inside that container sees these overrides; the rest of the application is unaffected.
| Role | Hex | Notes |
|---|---|---|
| Background / input background | #1E1E1E |
VS Code-style dark charcoal |
| Foreground (text) | #CECECE |
Light grey — easy on the eye against dark bg |
| Placeholder text | #7A7A7A |
Dimmer grey |
| Disabled text | #888888 |
Slightly lighter than placeholder |
| Text selection | #264F78 |
Muted blue (VS Code selection) |
| Cursor / focus | #CECECE |
Matches foreground |
| Input border | #3C3C3C |
Subtle, same family as bg |
| Scroll bar | #606060 / #2A2A2A |
Track and thumb |
The entry widget uses fyne.TextStyle{Monospace: true}. Fyne resolves monospace fonts from the host OS font stack at runtime using the following fallback order:
- Consolas (Windows default monospace; also present on many macOS installs)
- Courier New (bundled on all major platforms)
- System monospace fallback (Menlo on macOS, Monospace on Linux)
No TTF files are bundled; the OS font stack is used directly.
- Global colours / sizes → edit
gui/theme/macos.go. EachColor()andSize()case maps to a named Fyne constant (e.g.theme.ColorNamePrimary,theme.SizeNameText). Unlisted names fall back totheme.LightTheme(). - Viewer background / foreground → edit the
Viewer*colour variables at the top ofgui/theme/viewer.go. Only the colour tokens listed in itsColor()method are overridden; all others delegate to the active global theme.
| Platform | Requirement |
|---|---|
| macOS | Xcode Command Line Tools (xcode-select --install) — provides clang |
| Linux | gcc, libgl1-mesa-dev, xorg-dev (headers only, not bundled in binary) |
| Windows | TDM-GCC or MSYS2/MinGW-w64 (MIT licensed) |
CGo must be enabled (CGO_ENABLED=1, which is the default on the host platform).
There are two ways to build and run the GUI: go run / go build for development:
| Method | Output | macOS app-menu name | Windows taskbar name |
|---|---|---|---|
go run ./gui |
no binary | binary temp name | n/a |
go build -o sbom-utility-gui ./gui |
raw binary | sbom-utility-gui (file name) |
sbom-utility-gui |
# Run directly without producing a binary (app-menu shows temp binary name on macOS)
go run ./gui
# Build raw binary (app-menu shows "sbom-utility-gui" on macOS)
go build -o sbom-utility-gui ./gui
# Build CLI binary (unchanged)
go build -o sbom-utility .
# Build GUI binary via Make (output: sbom-utility-gui)
make build-guifyne package wraps the binary in a native bundle and injects gui/FyneApp.toml metadata into the platform manifest (Info.plist on macOS, AppxManifest.xml on Windows). That manifest is what the OS reads to display the app name in the menu bar, Dock, and taskbar — a raw binary never has one, so the OS falls back to the file name.
Install the Fyne CLI once:
go install fyne.io/tools/cmd/fyne@latestThen package for each platform. fyne package reads gui/FyneApp.toml automatically when run from the gui/ directory or when ./gui is passed as the target.
go install places binaries in $GOPATH/bin (default ~/go/bin), which is not on $PATH by default on macOS. Add it to your shell profile and reload:
# bash (~/.bash_profile or ~/.bashrc)
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.bash_profile && source ~/.bash_profile# zsh (~/.zshrc) — default shell on macOS Catalina and later
echo 'export PATH="$PATH:$(go env GOPATH)/bin"' >> ~/.zshrc && source ~/.zshrcVerify with which fyne — it should print ~/go/bin/fyne.
fyne package -os darwin -icon gui/images/icons/BOM-Utility-Icon.png ./gui
# Produces: SBOM Utility.app
# macOS reads Info.plist inside the bundle → menu bar shows "SBOM Utility"fyne package -os windows -icon gui/images/icons/BOM-Utility-Icon.png ./gui
# Produces: SBOM Utility.exe (no runtime DLLs needed)fyne package -os linux -icon gui/images/icons/BOM-Utility-Icon.png ./guifyne-cross (MIT) uses Docker to cross-compile for all targets without manual cross-toolchain setup:
go install github.com/fyne-io/fyne-cross@latest
fyne-cross darwin --arch=amd64,arm64 ./gui
fyne-cross windows --arch=amd64 ./gui
fyne-cross linux --arch=amd64,arm64 ./guiApplication identity is defined in gui/FyneApp.toml.
Fyne reads this file at build time and embeds it into the native bundle (Info.plist
on macOS, AppxManifest.xml on Windows), which is how the OS derives the name shown
in the app menu, Dock, and taskbar.
| Value | Where to edit |
|---|---|
| Application display name | [Details] Name in gui/FyneApp.toml |
| Bundle / app ID | [Details] ID in gui/FyneApp.toml |
| Version string | [Details] Version in gui/FyneApp.toml — also overridable at link time (see below) |
| Copyright year / holder | gui/main.go — inside the aboutItem callback |
| Project URL | gui/main.go — inside the aboutItem callback |
Note: When running as a plain binary (
go run ./gui/go build) macOS still shows the binary file name in the menu bar. TheFyneApp.tomlname only takes effect inside a packaged.appbundle (see Build → macOS above).
go build -ldflags "-X main.Version=$(git describe --tags --always)" -o sbom-utility-gui ./guiThe GUI code (gui/) is licensed under the Apache-2.0 license, matching the rest of sbom-utility.
The GUI depends on Fyne (BSD-3-Clause) and its transitive dependencies, all of which are permissively licensed (MIT, BSD-3, zlib, Apache-2.0). No GPL or LGPL code is introduced.