Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions memory-bank/backendApi.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Every command is `async`. Every command returns `Result<T, BrewError>` (serializ
|---|---|---|---|---|---|
| `brew_search` | `async fn brew_search(query: String) -> Result<SearchResults, BrewError>` | Searches the Homebrew index for matching formulae and casks. | `brew search --formula <q>` and `brew search --cask <q>` (parallel within the command; plain stdout — no `--json` support) | no | shared-read |
| `brew_search_desc` | `async fn brew_search_desc(query: String) -> Result<SearchResults, BrewError>` | Searches by description text. Optional / "Phase 2.1" if time permits. | `brew search --desc <q>` | no | shared-read |
| `local_search` | `async fn local_search(query: String, locale: Option<String>, state) -> Result<SearchResults, BrewError>` | In-memory search across package token/name, upstream desc, bundled enrichment, category labels, tags, and locale overlay search terms. `locale` selects a partial `enrichment.<locale>.json.gz` overlay when present, while English fields remain searchable fallbacks. | none — scans bundled/user-refreshed catalog already in memory | no | no |

### Phase 3 — Install / uninstall / upgrade (streaming)

Expand Down Expand Up @@ -1194,8 +1195,8 @@ All six commands share the `authed_gate` helper in `commands/github.rs:152` whic

| Command | Signature | Purpose | Paranoid gate | Auth | Source |
|---|---|---|---|---|---|
| `enrichment_data` | `async fn enrichment_data(state) -> Result<Arc<EnrichmentData>, BrewError>` | Returns the full bundled enrichment payload. Memoised on `AppState.enrichment_cache` — subsequent calls return an Arc clone, not a re-parse. Zero runtime LLM calls; the bundle is `include_bytes!`d at compile time. | no (bundled) | none | `commands/enrichment.rs:23` |
| `enrichment_lookup` | `async fn enrichment_lookup(name: String, state) -> Result<Option<EnrichmentEntry>, BrewError>` | Per-token lookup. `validate_package_name` gates the input. Returns `None` when the token is missing (placeholder bundle, unmapped package). | no | none | `commands/enrichment.rs:43` |
| `enrichment_data` | `async fn enrichment_data(locale: Option<String>, state) -> Result<Arc<EnrichmentData>, BrewError>` | Returns the full bundled enrichment payload, optionally merged with a small locale overlay such as `enrichment.ru.json.gz`. Base English data is memoised on `AppState.enrichment_cache`; localized copies are memoised by locale in `enrichment_locale_cache`. Zero runtime LLM calls; bundles are `include_bytes!`d at compile time. | no (bundled) | none | `commands/enrichment.rs:23` |
| `enrichment_lookup` | `async fn enrichment_lookup(name: String, locale: Option<String>, state) -> Result<Option<EnrichmentEntry>, BrewError>` | Per-token lookup with the same locale overlay semantics as `enrichment_data`. `validate_package_name` gates the input. Returns `None` when the token is missing (placeholder bundle, unmapped package). | no | none | `commands/enrichment.rs:65` |

### 13.10 New error variants

Expand Down Expand Up @@ -1364,4 +1365,3 @@ Critical invariants pinned:
- Cache corrupt → empty cache returned, no panic
- GHSA enrichment soft-fails when `github_enabled` is off
- Install-set fingerprint is deterministic across runs (sorted input → stable hash)

12 changes: 11 additions & 1 deletion native/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import PackageDescription
// `BrewBrowser` executable is wrapped into a launchable .app by build-app.sh.
let package = Package(
name: "BrewBrowser",
defaultLocalization: "en",
platforms: [.macOS(.v26)],
products: [
// Exposing the library as a product makes SwiftPM/Xcode generate a
Expand All @@ -34,7 +35,12 @@ let package = Package(
.executableTarget(
name: "BrewBrowser",
dependencies: ["BrewBrowserKit"],
path: "Sources/BrewBrowser"
path: "Sources/BrewBrowser",
resources: [
.process("Resources/Localizable.xcstrings"),
.process("Resources/en.lproj"),
.process("Resources/ru.lproj")
]
),
// Library: all views, AppModel, and the brew/vulns/github/trending/
// enrichment services. Bundled JSON resources live here alongside the
Expand All @@ -48,8 +54,12 @@ let package = Package(
],
path: "Sources/BrewBrowserKit",
resources: [
.process("Resources/Localizable.xcstrings"),
.process("Resources/en.lproj"),
.process("Resources/ru.lproj"),
.copy("Resources/categories.json"),
.copy("Resources/enrichment.json"),
.copy("Resources/enrichment.ru.json"),
// GitHub Octocat mark (vector PDF, Primer/Octicons MIT). Rendered
// as a template image in the toolbar's "connected" chip.
.copy("Resources/github-mark.pdf"),
Expand Down
30 changes: 15 additions & 15 deletions native/Sources/BrewBrowser/BrewBrowserApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ struct AppCommands: Commands {
// mirrors the Tauri AboutModal. A "Sponsor brew-browser…" item sits below
// so the donate CTA also lives in the app menu.
CommandGroup(replacing: .appInfo) {
Button("About Brew Browser") { model.aboutOpen = true }
Button("Sponsor Brew Browser…") {
Button(L10n.string("about.title")) { model.aboutOpen = true }
Button(L10n.string("chrome.donate.menu")) {
if let url = URL(string: AboutInfo.sponsorURL) {
NSWorkspace.shared.open(url)
}
Expand All @@ -79,50 +79,50 @@ struct AppCommands: Commands {
// Sparkle's documented SwiftUI integration. Mirrors the Tauri "Check now"
// (`SettingsSectionUpdates.svelte`).
CommandGroup(after: .appInfo) {
Button("Check for Updates…") { updater.checkForUpdates() }
Button(L10n.string("updates.checkForUpdates")) { updater.checkForUpdates() }
.disabled(!updater.canCheckForUpdates)
}

// A dedicated "Go" menu hosts the section-nav shortcuts (⌘0–6) so they
// appear together with their key equivalents, like a stock View menu.
CommandMenu("Go") {
Button("Dashboard") { model.go(toSectionNumber: 0) }
CommandMenu(L10n.string("menu.go")) {
Button(L10n.string("nav.dashboard")) { model.go(toSectionNumber: 0) }
.keyboardShortcut("0", modifiers: .command)
Button("Library") { model.go(toSectionNumber: 1) }
Button(L10n.string("nav.library")) { model.go(toSectionNumber: 1) }
.keyboardShortcut("1", modifiers: .command)
Button("Discover") { model.go(toSectionNumber: 2) }
Button(L10n.string("nav.discover")) { model.go(toSectionNumber: 2) }
.keyboardShortcut("2", modifiers: .command)
Button("Trending") { model.go(toSectionNumber: 3) }
Button(L10n.string("nav.trending")) { model.go(toSectionNumber: 3) }
.keyboardShortcut("3", modifiers: .command)
Button("Snapshots") { model.go(toSectionNumber: 4) }
Button(L10n.string("nav.snapshots")) { model.go(toSectionNumber: 4) }
.keyboardShortcut("4", modifiers: .command)
Button("Services") { model.go(toSectionNumber: 5) }
Button(L10n.string("nav.services")) { model.go(toSectionNumber: 5) }
.keyboardShortcut("5", modifiers: .command)
Button("Activity") { model.go(toSectionNumber: 6) }
Button(L10n.string("nav.activity")) { model.go(toSectionNumber: 6) }
.keyboardShortcut("6", modifiers: .command)

Divider()

// ⌘K command palette.
Button("Command Palette…") { model.paletteOpen = true }
Button(L10n.string("palette.title.menu")) { model.paletteOpen = true }
.keyboardShortcut("k", modifiers: .command)
}

// ⌘R refresh + ⌘L drawer toggle replace the stock toolbar/sidebar
// command group so they slot into the View menu.
CommandGroup(after: .toolbar) {
Button("Refresh") {
Button(L10n.string("action.refresh")) {
Task { await model.refreshCurrent() }
}
.keyboardShortcut("r", modifiers: .command)
.disabled(model.isLoading)

Button(model.drawerOpen ? "Hide Activity Drawer" : "Show Activity Drawer") {
Button(model.drawerOpen ? L10n.string("activity.drawer.hide") : L10n.string("activity.drawer.show")) {
model.toggleDrawer()
}
.keyboardShortcut("l", modifiers: .command)

Button("Cycle Theme") { model.cycleTheme() }
Button(L10n.string("theme.cycle")) { model.cycleTheme() }
.keyboardShortcut("l", modifiers: [.command, .shift])
}
}
Expand Down
Loading