Contents

nerdsnipe-inc/aibrowserkit

SwiftUI + WKWebView browser components for macOS agent applications. Embed a full multi-tab browser panel, pinned-site sidebar, bookmarks, JS console capture, and screenshot tooling — or pull in the WebKit primitives alone and build custom chrome around your own automation br

Features

| Feature | Description | |---------|-------------| | Multi-tab browser | BrowserView + BrowserViewModel + BrowserTab — tab bar, progress, smart URL bar | | Bookmarks | Folder tree, JSONL persistence, slide-out panel, horizontal bookmark bar | | Pinned sites | Sidebar tiles with groups, SF Symbols, favicons, auto-refresh, localhost reachability | | Console capture | Intercepts console.log/info/warn/error/debug into a ring buffer; detached panel or inline view | | Screenshots | Visible-area snapshot or drag-select region → clipboard, file, or host clipboard callback | | Stealth injection | Document-start script to reduce automation fingerprinting (see Security) | | Favicon cache | Disk + memory cache with Google S2 fallback | | Theme override | Cycle system / light / dark appearance per web view |


Platform support

AiBrowserKit declares iOS and visionOS in Package.swift, but the full browser UI is macOS-only (#if canImport(AppKit)). Cross-platform targets expose WebKit helpers and bookmark models for custom integrations.

| API / feature | macOS | iOS / visionOS | |---------------|:-----:|:--------------:| | BrowserView, BrowserEnvironment | ✅ | — | | Pinned sites UI, screenshots, console panel | ✅ | — | | WebViewFactory, WebViewRepresentable, WebViewState | ✅ | ✅ | | BookmarkService, BookmarkBarView, ConsoleLogView | ✅ | ✅ | | BrowserTab, BrowserViewModel | ✅ | — |

Deployment target: macOS 26.0 (matches Apple Intelligence / Tahoe SDK apps in the NerdSnipe stack). Lower the target in Package.swift if you need broader macOS support — most APIs are standard WebKit/SwiftUI.


Installation

Swift Package Manager

// Package.swift
dependencies: [
    .package(url: "https://github.com/NerdSnipe-Inc/AiBrowserKit.git", from: "1.0.0"),
],
targets: [
    .target(
        name: "MyApp",
        dependencies: ["AiBrowserKit"]
    ),
]

Local path dependency (monorepo / CI)

Point Package.swift at a sibling checkout:

MyWorkspace/
  MyApp/
  packages/AiBrowserKit/ ../packages/AiBrowserKit

Quick start — full browser (macOS)

Create a shared BrowserEnvironment at app root and inject it into BrowserView:

import SwiftUI
import AiBrowserKit

@main
struct MyApp: App {
    @State private var browserEnv = BrowserEnvironment()  // optional storageDirectory:

    var body: some Scene {
        WindowGroup {
            BrowserView()
                .environment(browserEnv)
        }
    }
}

Host clipboard callback

Wire screenshots and “copy to app clipboard” actions into your host store:

browserEnv.onAddToClipboard = { content in
    switch content.kind {
    case .text(let string):
        // Add to your app-wide clipboard / memory pipeline
        break
    case .image(let nsImage):
        // Handle NSImage from visible or region screenshot
        break
    }
}

Use ScreenshotDestination.hostClipboard or the navigation bar actions that call onAddToClipboard when set.


Quick start — custom panel + agent bridge

For agent apps that drive the browser via tools (browserNavigate, browserGetContent, browserExecute), you typically do not use BrowserView. Instead:

  1. Create a WKWebView with WebViewFactory.makeWebView(state:consoleStore:).
  2. Render it with WebViewRepresentable(webView:).
  3. Register the active web view with your host controller when the tab becomes visible.
import AiBrowserKit
import WebKit

let state = WebViewState()
let webView = WebViewFactory.makeWebView(state: state, consoleStore: myConsoleStore)

// In SwiftUI:
WebViewRepresentable(webView: webView)

// When this tab is active, register for agent tools:
HostBrowserController.shared.webView = webView
HostBrowserController.shared.webViewState = state

Implement navigation, content extraction, and JS evaluation in your host app’s browser controller. AiBrowserKit supplies the web view, state, and optional bookmark bar.


Architecture

┌─────────────────────────────────────────────────────────────┐
 Views (SwiftUI — macOS: BrowserView, PinnedSiteView, )     
└──────────────────────────┬──────────────────────────────────┘
 @Environment(BrowserEnvironment.self)
┌──────────────────────────▼──────────────────────────────────┐
 BrowserEnvironment                                           
  browserVM · bookmarks · favicons · pinnedSites · console     
└──────────┬─────────────────┬─────────────────┬──────────────┘

    ViewModels          Services            Models
 BrowserViewModel    BookmarkService     BrowserTab
 PinnedSiteStore     FaviconService      BrowserBookmark
                     PinnedSiteWebViewCache  PinnedSite
                                           ConsoleLogStore

┌──────────────────────────▼──────────────────────────────────┐
 WebKit/
  WebViewFactory · WebViewState · WebViewCoordinator          
  WebViewRepresentable · StealthScript (internal)             
└─────────────────────────────────────────────────────────────┘

Public types (selected)

| Type | Role | |------|------| | BrowserEnvironment | Root @Observable container; inject at app root | | BrowserViewModel | Tab list, selection, new/close tab | | BrowserTab | One tab: WKWebView + WebViewState + URL bar logic | | WebViewState | Observable page state (URL, title, loading, back/forward) | | WebViewFactory | Creates configured WKWebView with stealth + console scripts | | BookmarkService | JSONL bookmarks + JSON folders on disk | | PinnedSiteStore | Pinned sites and groups persistence | | ConsoleLogStore | Ring buffer (max 500 entries) for captured console output | | FaviconService | Fetch and cache site favicons |


Data storage

| Data | Default location | Configurable | |------|------------------|:------------:| | Bookmarks + folders | ~/Library/Application Support/AiBrowserKit/ | ✅ BookmarkService(storageDirectory:) | | Pinned sites | …/AiBrowserKit/pinned_sites.json | ✅ PinnedSiteStore(storageDirectory:) or BrowserEnvironment(storageDirectory:) | | Favicon cache | …/AiBrowserKit/favicons/ | ✅ same | | Bookmark UI state | UserDefaults key aibrowserkit.bookmarks.expandedFolderIDs | — |

Pass a single directory to BrowserEnvironment(storageDirectory:) to colocate all persisted browser data (bookmarks, pinned sites, favicons).


Console

Console messages are captured when WebViewFactory.makeWebView receives a non-nil ConsoleLogStore. The factory injects a script that forwards console.* to the native aiBrowserConsole message handler.

Detached panel (macOS):

ConsoleWindowManager.shared.toggle(store: browserEnv.consoleStore)

Inline:

ConsoleLogView(store: browserEnv.consoleStore)

Pinned sites

PinnedSiteView(site: site)
    .environment(browserEnv)

Supports groups, SF Symbol or favicon icons, color dots, optional auto-refresh interval, and a localhost reachability indicator (HEAD probe). PinnedSiteWebViewCache keeps live WKWebView instances per pinned site ID when switching sidebar selection.


Stealth mode

StealthScript (injected at .atDocumentStart, all frames) attempts to reduce automation fingerprinting:

  • Clears navigator.webdriver
  • Spoofs plugins, permissions, vendor strings
  • Adds canvas noise and WebGL vendor/renderer overrides
  • Cleans exposed window.webkit.messageHandlers after console handler setup

This is not a guarantee against bot detection. Host apps should document ethical use, respect site terms of service, and apply their own URL policies for agent-driven navigation.


Security & privacy

| Topic | Behavior | |-------|----------| | Shared cookie jar | All web views use WKWebsiteDataStore.default() — sessions are shared | | URL policy | No built-in allowlist; BrowserTab.navigate accepts http(s), localhost, and aibrowser:// | | JS execution | Host apps can call evaluateJavaScript on registered web views — treat as privileged | | Favicon fallback | Google S2 favicon API may leak visited hostnames | | User agent | Hardcoded Safari-like strings (macOS 15.5 / iOS 18.5); update periodically | | Autoplay | mediaTypesRequiringUserActionForPlayback = | | TLS | Standard WKWebView certificate validation only (no pinning) |

Agent integrations should validate URLs before navigation and never expose raw JS execution to untrusted model output without sandboxing.


Package layout

Sources/AiBrowserKit/
├── Environment/     BrowserEnvironment, AiBrowserClipboardContent
├── Models/          BrowserTab, BrowserBookmark, PinnedSite, ConsoleEntry, 
├── ViewModels/      BrowserViewModel, PinnedSiteStore
├── Services/        BookmarkService, FaviconService, PinnedSiteWebViewCache
├── Utilities/       BrowserURLResolver, AiBrowserStorage
├── WebKit/          WebViewFactory, WebViewState, StealthScript
└── Views/           BrowserView, BookmarkBarView, ConsoleLogView, 

Tests/AiBrowserKitTests/
├── BrowserURLResolverTests.swift   URL bar resolution (unit)
├── ModelTests.swift                Codable models, theme override (unit)
├── BookmarkServiceTests.swift      JSONL persistence (unit)
├── PinnedSiteStoreTests.swift      Pinned sites persistence (unit)
├── ConsoleAndViewModelTests.swift  Console ring buffer, tabs, web view cache (unit)
├── FaviconServiceTests.swift       Disk cache read/evict (unit, macOS)
├── IntegrationTests.swift          Live WKWebView + network (integration, macOS)
├── TestSupport.swift               Temp storage helpers, integration gate
└── IntegrationTestGate             via `RUN_INTEGRATION_TESTS=1`

Testing

AiBrowserKit ships an AiBrowserKitTests target with unit tests (always run) and integration tests (live WKWebView, network, disk I/O) gated on the RUN_INTEGRATION_TESTS environment variable.

Quick commands

# Unit tests only (default CI on pull requests)
swift test
bash scripts/ci-test.sh unit

# Unit + integration (live WebKit + network)
RUN_INTEGRATION_TESTS=1 swift test
bash scripts/ci-test.sh integration

# Contributor policy only (no Swift tests)
bash scripts/ci-test.sh policy
bash scripts/check-no-cursor-attribution.sh

The policy check scans tracked sources and commit messages for prohibited Cursor contributor attribution (see scripts/check-no-cursor-attribution.sh).

On NerdSnipe self-hosted runners, scripts/ci-test.sh auto-selects Xcode-beta when present (~/Applications/Xcode-beta.app).

Test inventory

| Suite | Type | What it covers | |-------|------|----------------| | BrowserURLResolver | Unit | URL bar: schemes, localhost, bare domains, Google search fallback | | Model Codable & computed properties | Unit | BrowserBookmark, PinnedSite, PinnedSiteStoreData, legacy decode | | WebViewThemeOverride | Unit | Theme cycle icons/labels | | BookmarkService persistence | Unit | Add, dedupe, toggle, folders, remove; JSONL round-trip | | PinnedSiteStore persistence | Unit | Add/remove/update, visibility, append sort order | | ConsoleLogStore | Unit | Append, clear, 500-entry ring buffer | | BrowserViewModel tab management | Unit | Initial tab, new/close tab, last-tab guard | | PinnedSiteWebViewCache | Unit | Per-site web view reuse and evict | | FaviconService — disk cache | Unit (macOS) | Load PNG from cache dir, evict | | WebViewFactory — live WKWebView | Integration | Load example.com, console capture, config defaults | | BrowserTab — live navigation | Integration | Domain navigation, Google search fallback | | FaviconService — network | Integration | Fetch + cache favicon for example.com | | BrowserEnvironment — colocated storage | Integration | Unified storage directory round-trip |

Integration suites use @Suite(..., .enabled(if: IntegrationTestGate.isEnabled)) so they are skipped (not failed) when the env var is unset.

CI

GitHub Actions workflow (.github/workflows/ci.yml):

  • Every PR / push: scripts/ci-test.sh unit
  • main push or manual dispatch (run_integration: true): scripts/ci-test.sh integration

Host apps that embed a custom browser panel should add their own integration tests for tool-driven navigation; AiBrowserKit tests cover the package in isolation.


Roadmap

  • [x] Package test target
  • [ ] iOS BrowserView port or explicit macOS-only product split
  • [ ] Document minimum macOS version after SDK stabilization

License

MIT — see LICENSE.

Package Metadata

Repository: nerdsnipe-inc/aibrowserkit

Default branch: main

README: README.md