native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
3.6 kB · 102 lines
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103import Dependenciesimport Foundationimport GlyphonKit
/// Dependency surface for the user-invoked "Suggest an Icon" flow./// Results are cached in memory per repository for the app session so/// reopening the picker shows the previous run instantly; only an/// explicit Regenerate spends another model call.nonisolated struct RepositorySymbolSuggestionClient: Sendable { var cachedSuggestions: @Sendable (_ repositoryID: Repository.ID) async -> RepositorySymbolSuggestions? /// Builds the input locally, runs GlyphonKit's recommender, caches /// and returns the result. Cooperatively cancellable — cancelling the /// surrounding task throws `CancellationError` promptly, even /// mid-model-call. var generateSuggestions: @Sendable ( _ repositoryID: Repository.ID, _ rootURL: URL, _ repositoryDisplayName: String ) async throws -> RepositorySymbolSuggestions}
/// Session-scoped engine behind the live client. An actor so the lazy/// GlyphonKit database load and the cache stay data-race free; nothing/// here is persisted to disk.private actor RepositorySymbolSuggestionEngine { static let shared = RepositorySymbolSuggestionEngine()
private var glyphon: Glyphon? private var cache: [Repository.ID: RepositorySymbolSuggestions] = [:]
func cached(_ repositoryID: Repository.ID) -> RepositorySymbolSuggestions? { cache[repositoryID] }
func generate( repositoryID: Repository.ID, rootURL: URL, repositoryDisplayName: String ) async throws -> RepositorySymbolSuggestions { let input = RepositorySuggestionInput.build( rootURL: rootURL, repositoryDisplayName: repositoryDisplayName ) let glyphon = try loadGlyphon() let recommendation = try await glyphon.recommend(from: input.text) let suggestions = RepositorySymbolSuggestions( primary: recommendation.symbol.name, alternates: recommendation.alternates.map(\.name), reason: recommendation.reason, source: input.source, usedAI: recommendation.usedAI ) cache[repositoryID] = suggestions return suggestions }
private func loadGlyphon() throws -> Glyphon { if let glyphon { return glyphon } // Loads the bundled symbol database (~9k symbols) once per session. let loaded = try Glyphon() glyphon = loaded return loaded }}
nonisolated enum RepositorySymbolSuggestionClientKey: DependencyKey { static var liveValue: RepositorySymbolSuggestionClient { RepositorySymbolSuggestionClient( cachedSuggestions: { repositoryID in await RepositorySymbolSuggestionEngine.shared.cached(repositoryID) }, generateSuggestions: { repositoryID, rootURL, repositoryDisplayName in try await RepositorySymbolSuggestionEngine.shared.generate( repositoryID: repositoryID, rootURL: rootURL, repositoryDisplayName: repositoryDisplayName ) } ) }
static var previewValue: RepositorySymbolSuggestionClient { noSuggestions } static var testValue: RepositorySymbolSuggestionClient { noSuggestions }
private static var noSuggestions: RepositorySymbolSuggestionClient { struct Unavailable: Error {} return RepositorySymbolSuggestionClient( cachedSuggestions: { _ in nil }, generateSuggestions: { _, _, _ in throw Unavailable() } ) }}
extension DependencyValues { nonisolated var repositorySymbolSuggestionClient: RepositorySymbolSuggestionClient { get { self[RepositorySymbolSuggestionClientKey.self] } set { self[RepositorySymbolSuggestionClientKey.self] = newValue } }}