package dev.stave.di import dev.stave.data.SessionStore import dev.stave.data.SettingsStore import dev.stave.lyrics.LyricsRepository import dev.stave.lyrics.LyricsViewModel import dev.stave.playlist.M3uPlaylistStore import dev.stave.playlist.M3uPlaylistWriter import dev.stave.ui.browse.BrowseViewModel import dev.stave.ui.events.UiEvents import dev.stave.ui.nowplaying.NowPlayingViewModel import dev.stave.ui.servers.ServersViewModel import dev.stave.upnp.AudioMetadataReader import dev.stave.upnp.ContentDirectoryClient import dev.stave.upnp.DidlParser import dev.stave.upnp.MediaCollector import org.koin.core.module.dsl.singleOf import org.koin.dsl.module /** * Shared Koin module — binds the UPnP layer, the playlist writer, and the ViewModels. * Platforms bind the UpnpServiceHolder and Renderer concretes themselves (see * desktopModule / androidModule). * * Design note: the VMs are bound as plain singletons rather than Koin-ViewModels because * Koin 4.0's multiplatform `viewModelOf` DSL has a type-constraint mismatch with the * JB `lifecycle-viewmodel` KMP artifact in this configuration. App-scoped singletons are * fine for a desktop app that shows one of each screen at a time — the VM survives * navigation, which is actually useful here (caching the browse state). * * Resolution in composables: `koinInject()` (not `koinViewModel()`). */ val sharedModule = module { singleOf(::DidlParser) single { ContentDirectoryClient(holder = get(), parser = get(), settings = get()) } singleOf(::MediaCollector) singleOf(::M3uPlaylistWriter) single { M3uPlaylistStore() } singleOf(::AudioMetadataReader) single { val settings: SettingsStore = get() dev.stave.upnp.AlbumInfoLoader( reader = get(), allowExternalCalls = { settings.allowExternalCalls.value }, ) } single { dev.stave.ui.albuminfo.AlbumInfoViewModel(loader = get()) } single { LyricsRepository(get()) } single { SettingsStore() } single { SessionStore() } single { dev.stave.data.SearchHistoryStore() } single { UiEvents() } single { dev.stave.playback.LocalPlaybackGate(rawCast = get(), uiEvents = get()) } single { BrowseViewModel( cd = get(), renderer = get(), collector = get(), playGate = get(), uiEvents = get(), holder = get(), ) } // Explicit get() forms for NowPlayingViewModel + LyricsViewModel: their // constructor arg counts (7 and 2 respectively) push against Koin 4's // reflective singleOf reliability. The typed get() calls resolve at module // build time, so any missing binding surfaces there rather than at first use. single { NowPlayingViewModel( renderer = get(), rawCast = get(), cd = get(), collector = get(), playlistWriter = get(), playlistStore = get(), sessionStore = get(), settings = get(), uiEvents = get(), playGate = get(), ) } single { dev.stave.upnp.MinimServerControl(holder = get()) } single { ServersViewModel(holder = get(), cd = get(), minim = get()) } single { LyricsViewModel(repository = get(), renderer = get(), rawCast = get(), settings = get()) } }