diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index e6a97a129523e05dbf396364f3ee2d50f694f499..c791bf08c6f62877ca74ed6b3a67a60d559c2cad 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -156,6 +156,7 @@ dependencies {
     implementation(libs.acra.limiter)
 
     implementation(libs.commonutils.core)
+    implementation(libs.commonutils.compose)
     implementation(libs.commonutils.voyager)
 
     dokkaPlugin(libs.dokka.android)
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/App.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/App.kt
index 1eb22a529fdbad99e6a8565de183fe6ec53df7ec..4f2918c44c801b0fade1b52a0f29a5f776529d2b 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/App.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/App.kt
@@ -11,12 +11,12 @@ import kotlinx.coroutines.MainScope
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.plus
 import kotlinx.coroutines.runBlocking
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.android.toastShort
 import net.novagamestudios.common_utils.compose.application
-import net.novagamestudios.common_utils.error
-import net.novagamestudios.common_utils.info
-import net.novagamestudios.common_utils.toastShort
-import net.novagamestudios.common_utils.warn
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.error
+import net.novagamestudios.common_utils.logging.info
+import net.novagamestudios.common_utils.logging.warn
 import net.novagamestudios.kaffeekasse.api.hiwi_tracker.HiwiTrackerAPI
 import net.novagamestudios.kaffeekasse.api.hiwi_tracker.HiwiTrackerScraper
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.KaffeekasseAPI
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/MainActivity.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/MainActivity.kt
index eea4033c523d7321680c6790fb64a36c77c3c7ce..cf3fc51985ec182818aadb7f4aff157719f431fe 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/MainActivity.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/MainActivity.kt
@@ -14,7 +14,7 @@ import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
-import net.novagamestudios.common_utils.LocalLogger
+import net.novagamestudios.common_utils.logging.LocalLogger
 import net.novagamestudios.kaffeekasse.repositories.SettingsRepository
 import net.novagamestudios.kaffeekasse.ui.App
 import net.novagamestudios.kaffeekasse.ui.util.removeScrollableTabRowMinimumTabWidth
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/UpdateReciever.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/UpdateReciever.kt
index 46ad143eccb57661a96b92ff1863e586451e9eab..a671abee299f94f76bcb9d7847e861ac8f23471f 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/UpdateReciever.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/UpdateReciever.kt
@@ -3,8 +3,8 @@ package net.novagamestudios.kaffeekasse
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.info
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.info
 
 
 class UpdateReceiver : BroadcastReceiver(), Logger {
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/api/hiwi_tracker/HiwiTrackerAPI.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/api/hiwi_tracker/HiwiTrackerAPI.kt
index be4b62b1a3a49b38d72322f6dfd43c32c536eefc..e9b9fc0aa252f141cc8e47bc62dcb69def0b11d3 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/api/hiwi_tracker/HiwiTrackerAPI.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/api/hiwi_tracker/HiwiTrackerAPI.kt
@@ -2,9 +2,9 @@ package net.novagamestudios.kaffeekasse.api.hiwi_tracker
 
 import io.ktor.http.parameters
 import kotlinx.datetime.format
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.kaffeekasse.api.portal.PortalClient
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.kaffeekasse.api.hiwi_tracker.model.MonthDataResponse
+import net.novagamestudios.kaffeekasse.api.portal.PortalClient
 import net.novagamestudios.kaffeekasse.model.hiwi_tracker.MonthKey
 
 
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/KaffeekasseAPI.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/KaffeekasseAPI.kt
index 8e8d1767ed7315d71e32616eef0c59a7b902aa4e..67c37a6938104df782f84b467a1f63bf48fde274 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/KaffeekasseAPI.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/KaffeekasseAPI.kt
@@ -3,10 +3,9 @@ package net.novagamestudios.kaffeekasse.api.kaffeekasse
 import io.ktor.http.Parameters
 import io.ktor.http.parameters
 import io.ktor.util.sha1
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.error
-import net.novagamestudios.kaffeekasse.api.portal.PortalClient
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.error
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.ItemListResponse
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.KaffeekasseAPIResponse
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.LoggedInUserResponse
@@ -16,6 +15,7 @@ import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.LogoutUserResponse
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.PurchaseResponse
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.UserInfoResponse
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.UserListResponse
+import net.novagamestudios.kaffeekasse.api.portal.PortalClient
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.KaffeekasseAPIResponse.Error.Code as ErrorCode
 
 class KaffeekasseAPI(
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/KaffeekasseScraper.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/KaffeekasseScraper.kt
index 76b51fb1264977914fa6ea4245b18263d3721a5d..75cdb85653bab36de3bc608bc063d79e7e03491e 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/KaffeekasseScraper.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/KaffeekasseScraper.kt
@@ -12,7 +12,7 @@ import it.skrape.selects.html5.select
 import it.skrape.selects.html5.table
 import it.skrape.selects.html5.td
 import it.skrape.selects.html5.tr
-import net.novagamestudios.common_utils.info
+import net.novagamestudios.common_utils.logging.info
 import net.novagamestudios.kaffeekasse.api.portal.PortalClient
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Cart
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.KnownItem
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/model/BasicUserInfo.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/model/BasicUserInfo.kt
index 3e2b94425645f533a870493706a34dd241d342b3..fee012cba5e8300c6293fe12821f8f5cac30daee 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/model/BasicUserInfo.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/model/BasicUserInfo.kt
@@ -2,8 +2,8 @@ package net.novagamestudios.kaffeekasse.api.kaffeekasse.model
 
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
+import net.novagamestudios.common_utils.serialization.BooleanAsIntSerializer
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Name
-import net.novagamestudios.kaffeekasse.util.IntAsBooleanSerializer
 
 @Serializable
 data class BasicUserInfo(
@@ -12,7 +12,7 @@ data class BasicUserInfo(
     @SerialName("name")
     override val name: Name,
     @SerialName("empty_pin")
-    val noPinSet: @Serializable(with = IntAsBooleanSerializer::class) Boolean? = null
+    val noPinSet: @Serializable(with = BooleanAsIntSerializer::class) Boolean? = null
 ) : APIPurchaseAccount {
     val mayHavePin get() = noPinSet == null || !noPinSet
 }
\ No newline at end of file
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/model/ItemListResponse.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/model/ItemListResponse.kt
index 6960eb3b2a2b9624719b51d7ddf9c36e683a3891..3bcb65e111f9cb0c3c8b2fea8d196bd2c0851743 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/model/ItemListResponse.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/api/kaffeekasse/model/ItemListResponse.kt
@@ -3,7 +3,7 @@ package net.novagamestudios.kaffeekasse.api.kaffeekasse.model
 import kotlinx.serialization.SerialName
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.JsonElement
-import net.novagamestudios.kaffeekasse.util.IntAsBooleanSerializer
+import net.novagamestudios.common_utils.serialization.BooleanAsIntSerializer
 
 @Serializable
 data class ItemListResponse(
@@ -25,7 +25,7 @@ data class ItemListResponse(
         val sortP: Double,
         @SerialName("itemtype_id")
         val itemTypeId: Int,
-        val enabled: @Serializable(with = IntAsBooleanSerializer::class) Boolean,
+        val enabled: @Serializable(with = BooleanAsIntSerializer::class) Boolean,
         @SerialName("has_condition_reports")
         val hasConditionReports: JsonElement
     )
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/api/portal/PortalClient.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/api/portal/PortalClient.kt
index 220c91ec8a9aa7dbc301c201c4dc6ee940a59f38..722892d66f83c314f26370994e4202e2a3b91a8b 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/api/portal/PortalClient.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/api/portal/PortalClient.kt
@@ -33,12 +33,12 @@ import kotlinx.serialization.json.Json
 import kotlinx.serialization.json.JsonElement
 import kotlinx.serialization.json.JsonObject
 import kotlinx.serialization.json.decodeFromJsonElement
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.error
-import net.novagamestudios.common_utils.info
-import net.novagamestudios.common_utils.verbose
-import net.novagamestudios.common_utils.warn
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.error
+import net.novagamestudios.common_utils.logging.info
+import net.novagamestudios.common_utils.logging.verbose
+import net.novagamestudios.common_utils.logging.warn
 import net.novagamestudios.kaffeekasse.api.portal.model.PortalAPIResponse
 import net.novagamestudios.kaffeekasse.model.credentials.Login
 import net.novagamestudios.kaffeekasse.model.credentials.isValid
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/model/hiwi_tracker/MonthKey.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/model/hiwi_tracker/MonthKey.kt
index 6f1113a88aadade00e66a9467920d7d1c1de078d..9c41025c31aaacc45a53abec1d0472d31c3691e6 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/model/hiwi_tracker/MonthKey.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/model/hiwi_tracker/MonthKey.kt
@@ -6,7 +6,7 @@ import kotlinx.datetime.LocalDate
 import kotlinx.datetime.Month
 import kotlinx.datetime.number
 import kotlinx.serialization.Serializable
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.logging.Logger
 
 
 @Serializable
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/model/hiwi_tracker/WorkEntry.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/model/hiwi_tracker/WorkEntry.kt
index 865b1c11e1bfa03c5ca169c0d616ed713cb2db9c..75612ddf0e75a14721457768316ceef51fa7e8ad 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/model/hiwi_tracker/WorkEntry.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/model/hiwi_tracker/WorkEntry.kt
@@ -2,7 +2,7 @@ package net.novagamestudios.kaffeekasse.model.hiwi_tracker
 
 import kotlinx.datetime.LocalDate
 import kotlinx.datetime.LocalTime
-import net.novagamestudios.kaffeekasse.util.minus
+import net.novagamestudios.common_utils.minus
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.hours
 import kotlin.time.Duration.Companion.minutes
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/Credentials.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/Credentials.kt
index 44c20473d2786b93707d564744cf051938578004..4e28f13c46adbeb880883c7ead3a88d929777be1 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/Credentials.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/Credentials.kt
@@ -11,12 +11,12 @@ import androidx.credentials.exceptions.CreateCredentialException
 import androidx.credentials.exceptions.CreateCredentialUnsupportedException
 import androidx.credentials.exceptions.GetCredentialCancellationException
 import androidx.credentials.exceptions.GetCredentialException
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.error
-import net.novagamestudios.common_utils.info
-import net.novagamestudios.common_utils.toastShort
-import net.novagamestudios.common_utils.verbose
+import net.novagamestudios.common_utils.android.toastShort
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.error
+import net.novagamestudios.common_utils.logging.info
+import net.novagamestudios.common_utils.logging.verbose
 import net.novagamestudios.kaffeekasse.BuildConfig
 import net.novagamestudios.kaffeekasse.model.credentials.DeviceCredentials
 import net.novagamestudios.kaffeekasse.model.credentials.Login
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/LoginRepository.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/LoginRepository.kt
index 421ae736a2ecb1da986e12382be658594ab5c47c..61fd147d4e9d45de83b0a60cca7b4bd009ddb60c 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/LoginRepository.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/LoginRepository.kt
@@ -4,12 +4,14 @@ import android.content.Context
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.sync.Mutex
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.info
-import net.novagamestudios.common_utils.toastShort
-import net.novagamestudios.common_utils.verbose
-import net.novagamestudios.common_utils.warn
+import net.novagamestudios.common_utils.android.toastShort
+import net.novagamestudios.common_utils.collection.mapState
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.info
+import net.novagamestudios.common_utils.logging.verbose
+import net.novagamestudios.common_utils.logging.warn
+import net.novagamestudios.common_utils.withReentrantLock
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.KaffeekasseAPI
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.BasicUserInfo
 import net.novagamestudios.kaffeekasse.model.credentials.DeviceCredentials
@@ -19,8 +21,6 @@ import net.novagamestudios.kaffeekasse.model.kaffeekasse.UserAuthCredentials
 import net.novagamestudios.kaffeekasse.model.session.Session
 import net.novagamestudios.kaffeekasse.repositories.i11.KaffeekasseRepository
 import net.novagamestudios.kaffeekasse.repositories.i11.PortalRepository
-import net.novagamestudios.kaffeekasse.util.mapState
-import net.novagamestudios.kaffeekasse.util.withReentrantLock
 
 class LoginRepository(
     private val credentialsRepository: Credentials,
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/Settings.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/Settings.kt
index c80b801d8860c1a6f49e76a9f12186a7640b5a0c..940db21350e34b6bfad9ea790d303bb45b369a17 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/Settings.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/Settings.kt
@@ -11,10 +11,10 @@ import kotlinx.serialization.Serializable
 import kotlinx.serialization.json.Json
 import kotlinx.serialization.modules.SerializersModule
 import kotlinx.serialization.serializer
-import net.novagamestudios.common_utils.JsonToDataStore
 import net.novagamestudios.common_utils.compose.state.DataStoreState
 import net.novagamestudios.common_utils.compose.state.MutableDataStoreState
 import net.novagamestudios.common_utils.compose.state.stateIn
+import net.novagamestudios.common_utils.serialization.JsonToDataStore
 import net.novagamestudios.kaffeekasse.model.credentials.DeviceCredentials
 import net.novagamestudios.kaffeekasse.ui.util.derived
 import java.io.File
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/SettingsRepository.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/SettingsRepository.kt
index b586c6cad1827c6407dda022f7d7d502c4ec02b9..3ebfeb3bb49aaf1bc3a5e9d803bcbf411beb5a4d 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/SettingsRepository.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/SettingsRepository.kt
@@ -1,9 +1,9 @@
 package net.novagamestudios.kaffeekasse.repositories
 
 import kotlinx.coroutines.flow.StateFlow
+import net.novagamestudios.common_utils.collection.mapState
 import net.novagamestudios.common_utils.compose.state.MutableDataStoreState
 import net.novagamestudios.kaffeekasse.model.session.realUserOrNull
-import net.novagamestudios.kaffeekasse.util.mapState
 
 class SettingsRepository(
     repositoryProvider: RepositoryProvider,
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/UpdateController.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/UpdateController.kt
index 0579bc8ae27b32b52ff14a23ab20c707469bee40..ffcc855c3860902a4f7dad3326886aa3b7dcc4bd 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/UpdateController.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/UpdateController.kt
@@ -11,11 +11,11 @@ import io.ktor.http.contentLength
 import io.ktor.utils.io.ByteReadChannel
 import io.ktor.utils.io.jvm.javaio.copyTo
 import kotlinx.coroutines.flow.MutableStateFlow
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.common_utils.compose.components.Progress
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.error
-import net.novagamestudios.common_utils.info
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.error
+import net.novagamestudios.common_utils.logging.info
 import net.novagamestudios.kaffeekasse.MainActivity
 import net.novagamestudios.kaffeekasse.UpdateReceiver
 import net.novagamestudios.kaffeekasse.model.app.AppRelease
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/HiwiTrackerRepository.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/HiwiTrackerRepository.kt
index 7f16d4635523b1fc44d56e847183248ace4f2ae5..8dc5bab4cbee58de04d0475a4d4d0d3c094d52fc 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/HiwiTrackerRepository.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/HiwiTrackerRepository.kt
@@ -1,8 +1,8 @@
 package net.novagamestudios.kaffeekasse.repositories.i11
 
 import kotlinx.coroutines.CoroutineScope
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
 import net.novagamestudios.kaffeekasse.api.hiwi_tracker.HiwiTrackerAPI
 import net.novagamestudios.kaffeekasse.api.hiwi_tracker.HiwiTrackerScraper
 import net.novagamestudios.kaffeekasse.api.hiwi_tracker.model.MonthDataResponse
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/KaffeekasseRepository.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/KaffeekasseRepository.kt
index 451c832b7c617ff3e3925efe8fadfc7d9832da8a..3a1e29b5f1146b72d486263ecd711f10f01331e4 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/KaffeekasseRepository.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/KaffeekasseRepository.kt
@@ -3,7 +3,7 @@ package net.novagamestudios.kaffeekasse.repositories.i11
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.KaffeekasseAPI
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.KaffeekasseScraper
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.APIPurchaseAccount
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/PortalRepository.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/PortalRepository.kt
index 48d3c7ad185788ebbb84409aaf84854f95275a84..99a376d330f0b2a0b4d7192aa7d8117c1f733291 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/PortalRepository.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/i11/PortalRepository.kt
@@ -4,10 +4,10 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.stateIn
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.info
-import net.novagamestudios.common_utils.warn
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.info
+import net.novagamestudios.common_utils.logging.warn
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.KaffeekasseAPI
 import net.novagamestudios.kaffeekasse.api.portal.PortalClient
 import net.novagamestudios.kaffeekasse.model.credentials.Login
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/releases/GitLabPackageReleases.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/releases/GitLabPackageReleases.kt
index 0c4189ebc1d4599d823b2313b392efa5015d1591..ca0aae25ffd2c3d776c4b586bd6a89955a86800a 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/releases/GitLabPackageReleases.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/releases/GitLabPackageReleases.kt
@@ -1,9 +1,9 @@
 package net.novagamestudios.kaffeekasse.repositories.releases
 
 import kotlinx.coroutines.flow.MutableStateFlow
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.info
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.info
 import net.novagamestudios.kaffeekasse.gitlab.GitLab
 import net.novagamestudios.kaffeekasse.gitlab.GitLabPackage
 import net.novagamestudios.kaffeekasse.gitlab.GitLabPackageFile
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/releases/GitLabReleases.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/releases/GitLabReleases.kt
index d6751153b695f91b6bf644f3beb130b89227513a..52cdbc8e12b0ae0cda3c9b879fa04cd3e8ce4e2e 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/releases/GitLabReleases.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/repositories/releases/GitLabReleases.kt
@@ -1,9 +1,9 @@
 package net.novagamestudios.kaffeekasse.repositories.releases
 
 import kotlinx.coroutines.flow.MutableStateFlow
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.info
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.info
 import net.novagamestudios.kaffeekasse.gitlab.GitLab
 import net.novagamestudios.kaffeekasse.model.app.AppRelease
 import net.novagamestudios.kaffeekasse.model.app.AppVersion
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/App.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/App.kt
index e730cac502d3c4b0e6fecb0f34c578564775b3f3..9ff0445c9c3989572b3b387589a9ee20159ff8b1 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/App.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/App.kt
@@ -16,15 +16,16 @@ import androidx.compose.ui.Modifier
 import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.screen.Screen
 import cafe.adriel.voyager.navigator.Navigator
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.LoggerForFun
 import net.novagamestudios.common_utils.compose.components.BoxCenter
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.verbose
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.LoggerForFun
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.verbose
 import net.novagamestudios.common_utils.voyager.model.GlobalScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.common_utils.voyager.model.collectAsStateHere
 import net.novagamestudios.common_utils.voyager.model.getValue
+import net.novagamestudios.common_utils.voyager.requireWithKey
 import net.novagamestudios.kaffeekasse.App
 import net.novagamestudios.kaffeekasse.model.session.Session
 import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
@@ -33,7 +34,6 @@ import net.novagamestudios.kaffeekasse.ui.navigation.AppModulesScreen
 import net.novagamestudios.kaffeekasse.ui.navigation.AppScreenTransition
 import net.novagamestudios.kaffeekasse.ui.navigation.LoginNavigation
 import net.novagamestudios.kaffeekasse.ui.theme.KaffeekasseTheme
-import net.novagamestudios.kaffeekasse.ui.util.navigation.requireWithKey
 
 
 class AppScreenModel private constructor(
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/AppModules.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/AppModules.kt
index 3c91f642899740bcdd21c196025cdd4d752477f9..31d362ac170dda6149654bbc2612c4f61d3de5aa 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/AppModules.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/AppModules.kt
@@ -30,6 +30,7 @@ import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.collectAsStateHere
+import net.novagamestudios.common_utils.voyager.nearestScreen
 import net.novagamestudios.kaffeekasse.AppModule
 import net.novagamestudios.kaffeekasse.AppModules
 import net.novagamestudios.kaffeekasse.HiwiTrackerModule
@@ -41,7 +42,6 @@ import net.novagamestudios.kaffeekasse.ui.navigation.AppModulesScreen
 import net.novagamestudios.kaffeekasse.ui.navigation.HiwiTrackerNavigation
 import net.novagamestudios.kaffeekasse.ui.navigation.KaffeekasseNavigation
 import net.novagamestudios.kaffeekasse.ui.navigation.ModuleTab
-import net.novagamestudios.kaffeekasse.ui.util.navigation.nearestScreen
 
 
 class AppModulesScreenModel private constructor(
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/Updates.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/Updates.kt
index e19d2417d19ef0732f8a45b5bc0c01679a91d522..1c8dad9e7406b699431219245aa317602103a571 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/Updates.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/Updates.kt
@@ -46,19 +46,19 @@ import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.model.screenModelScope
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.android.toastLong
 import net.novagamestudios.common_utils.compose.DashedShape
+import net.novagamestudios.common_utils.compose.Toasts
+import net.novagamestudios.common_utils.compose.ToastsState
 import net.novagamestudios.common_utils.compose.components.CircularLoadingBox
 import net.novagamestudios.common_utils.compose.components.ColumnCenter
 import net.novagamestudios.common_utils.compose.components.LinearProgressIndicator
 import net.novagamestudios.common_utils.compose.components.RowCenter
-import net.novagamestudios.common_utils.compose.components.Toasts
-import net.novagamestudios.common_utils.compose.components.ToastsState
 import net.novagamestudios.common_utils.compose.components.TransparentListItem
 import net.novagamestudios.common_utils.compose.state.ReentrantActionState
 import net.novagamestudios.common_utils.compose.state.collectAsStateIn
-import net.novagamestudios.common_utils.error
-import net.novagamestudios.common_utils.toastLong
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.error
 import net.novagamestudios.common_utils.voyager.model.GlobalScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.common_utils.voyager.model.getValue
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/EnterWorkingHours.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/EnterWorkingHours.kt
index 8ea31df70592760ac74affc31dd5f12c427d0667..9c20b6fa47f410d68c0ecbe0df53422e39a16a93 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/EnterWorkingHours.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/EnterWorkingHours.kt
@@ -74,16 +74,17 @@ import kotlinx.datetime.format.MonthNames
 import kotlinx.datetime.format.Padding
 import kotlinx.datetime.toKotlinLocalDate
 import kotlinx.datetime.toKotlinLocalTime
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.compose.Toasts
+import net.novagamestudios.common_utils.compose.ToastsState
 import net.novagamestudios.common_utils.compose.components.BoxCenter
 import net.novagamestudios.common_utils.compose.components.CircularLoadingBox
 import net.novagamestudios.common_utils.compose.components.ColumnCenter
 import net.novagamestudios.common_utils.compose.components.RowCenter
-import net.novagamestudios.common_utils.compose.components.Toasts
-import net.novagamestudios.common_utils.compose.components.ToastsState
 import net.novagamestudios.common_utils.compose.state.ReentrantActionState
 import net.novagamestudios.common_utils.compose.thenIf
-import net.novagamestudios.common_utils.warn
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.warn
+import net.novagamestudios.common_utils.minus
 import net.novagamestudios.kaffeekasse.R
 import net.novagamestudios.kaffeekasse.model.hiwi_tracker.WorkEntry
 import net.novagamestudios.kaffeekasse.model.hiwi_tracker.WorkEntry.Companion.MaxWorkWithoutLargeBreak
@@ -98,7 +99,6 @@ import net.novagamestudios.kaffeekasse.ui.theme.disabled
 import net.novagamestudios.kaffeekasse.ui.util.ClockFace
 import net.novagamestudios.kaffeekasse.ui.util.TimePickerState
 import net.novagamestudios.kaffeekasse.ui.util.monochrome
-import net.novagamestudios.kaffeekasse.util.minus
 import kotlin.math.roundToInt
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.hours
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/HiwiTrackerModule.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/HiwiTrackerModule.kt
index 6c80049a7c897e3147f2a5f293d5967100f204d5..e236ab51adc78aec98389a17cdd66e357bb2f239 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/HiwiTrackerModule.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/HiwiTrackerModule.kt
@@ -1,15 +1,13 @@
 package net.novagamestudios.kaffeekasse.ui.hiwi_tracker
 
-import androidx.compose.runtime.Composable
 import cafe.adriel.voyager.core.model.ScreenModel
-import cafe.adriel.voyager.navigator.Navigator
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.kaffeekasse.model.session.Session
 import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.HiwiTrackerNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
 
 
 class HiwiTrackerModuleScreenModel private constructor(
@@ -24,7 +22,4 @@ class HiwiTrackerModuleScreenModel private constructor(
     }
 }
 
-class HiwiTrackerModuleContent(provider: ScreenModelProvider<HiwiTrackerModuleScreenModel>) : ScaffoldContentWithModel<HiwiTrackerModuleScreenModel>(provider) {
-    @Composable
-    override fun Content(navigator: Navigator) { }
-}
+class HiwiTrackerModuleContent(provider: ScreenModelProvider<HiwiTrackerModuleScreenModel>) : AppScaffoldContentWithModel<HiwiTrackerModuleScreenModel>(provider)
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/Overview.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/Overview.kt
index 1cee1d6495aaf6de06d75e6c167b8742f2a53b7a..3917787df2669c15e2b4fe9559f80591c9887156 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/Overview.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/hiwi_tracker/Overview.kt
@@ -81,13 +81,14 @@ import kotlinx.datetime.format.Padding
 import kotlinx.datetime.format.char
 import kotlinx.datetime.toKotlinLocalTime
 import kotlinx.datetime.todayIn
-import net.novagamestudios.common_utils.Logger
 import net.novagamestudios.common_utils.compose.DashedShape
 import net.novagamestudios.common_utils.compose.components.BoxCenter
 import net.novagamestudios.common_utils.compose.components.CircularProgressIndicator
 import net.novagamestudios.common_utils.compose.components.RowCenter
-import net.novagamestudios.common_utils.debug
-import net.novagamestudios.common_utils.verbose
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
+import net.novagamestudios.common_utils.logging.verbose
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.kaffeekasse.api.hiwi_tracker.model.MonthDataResponse
@@ -103,13 +104,12 @@ import net.novagamestudios.kaffeekasse.ui.AppInfoTopBarAction
 import net.novagamestudios.kaffeekasse.ui.AppModuleSelection
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.FailureRetryScreen
 import net.novagamestudios.kaffeekasse.ui.login.LogoutTopBarAction
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.HiwiTrackerNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.theme.disabled
 import net.novagamestudios.kaffeekasse.ui.util.HorizontalKeyedPager
 import net.novagamestudios.kaffeekasse.ui.util.HorizontalPagedLayout
 import net.novagamestudios.kaffeekasse.ui.util.KeyedPagerState
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
 import net.novagamestudios.kaffeekasse.ui.util.onClickComingSoon
 import net.novagamestudios.kaffeekasse.ui.util.synchronizePagerState
 import net.novagamestudios.kaffeekasse.util.richdata.RichData
@@ -213,10 +213,7 @@ class OverviewScreenModel private constructor(
 }
 
 
-class OverviewContent(provider: ScreenModelProvider<OverviewScreenModel>) : ScaffoldContentWithModel<OverviewScreenModel>(provider) {
-    @Composable
-    override fun Content(navigator: Navigator) = Overview(model)
-
+class OverviewContent(provider: ScreenModelProvider<OverviewScreenModel>) : AppScaffoldContentWithModel<OverviewScreenModel>(provider) {
     @Composable
     override fun TopAppBarTitle(navigator: Navigator) {
         AppModuleSelection()
@@ -236,7 +233,7 @@ class OverviewContent(provider: ScreenModelProvider<OverviewScreenModel>) : Scaf
 
 
 @Composable
-private fun Overview(
+fun Overview(
     model: OverviewScreenModel,
     modifier: Modifier = Modifier
 ) = PullToRefreshBox(
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/Account.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/Account.kt
index 9c4349de15bf1b78031f95fc1e0a4ca2f202eedc..4934d911edb1583afbfbc6f39dbc341bd3eb7cbe 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/Account.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/Account.kt
@@ -25,11 +25,11 @@ import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.navigator.Navigator
-import net.novagamestudios.common_utils.Logger
 import net.novagamestudios.common_utils.compose.components.BoxCenter
 import net.novagamestudios.common_utils.compose.components.CircularProgressIndicator
 import net.novagamestudios.common_utils.compose.components.ColumnCenter
 import net.novagamestudios.common_utils.format
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Account
@@ -38,13 +38,13 @@ import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
 import net.novagamestudios.kaffeekasse.repositories.i11.KaffeekasseRepository
 import net.novagamestudios.kaffeekasse.ui.AppInfoTopBarAction
 import net.novagamestudios.kaffeekasse.ui.login.LogoutTopBarAction
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.AppSubpageTitle
 import net.novagamestudios.kaffeekasse.ui.navigation.KaffeekasseNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.util.FailureRetryScreen
 import net.novagamestudios.kaffeekasse.ui.util.PullToRefreshBox
 import net.novagamestudios.kaffeekasse.ui.util.RichDataContent
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
 import net.novagamestudios.kaffeekasse.util.richdata.collectAsRichStateHere
 
 class AccountScreenModel private constructor(
@@ -63,10 +63,7 @@ class AccountScreenModel private constructor(
     }
 }
 
-class AccountContent(provider: ScreenModelProvider<AccountScreenModel>) : ScaffoldContentWithModel<AccountScreenModel>(provider) {
-    @Composable
-    override fun Content(navigator: Navigator) = Account(model)
-
+class AccountContent(provider: ScreenModelProvider<AccountScreenModel>) : AppScaffoldContentWithModel<AccountScreenModel>(provider) {
     @Composable
     override fun TopAppBarTitle(navigator: Navigator) {
         AppSubpageTitle("Konto")
@@ -86,7 +83,7 @@ class AccountContent(provider: ScreenModelProvider<AccountScreenModel>) : Scaffo
 
 
 @Composable
-private fun Account(
+fun Account(
     model: AccountScreenModel,
     modifier: Modifier = Modifier
 ) = PullToRefreshBox(
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/KaffeekasseModule.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/KaffeekasseModule.kt
index c2c331ac449d4b9a2d036999f54e8ea72377895f..1271439e355097a413a93eb7953d33bf1501134e 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/KaffeekasseModule.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/KaffeekasseModule.kt
@@ -10,17 +10,17 @@ import androidx.compose.material3.IconButton
 import androidx.compose.runtime.Composable
 import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.navigator.Navigator
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.kaffeekasse.KaffeekasseModule.Companion.kaffeekasseCartProvider
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.MutableCart
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.isNotEmpty
 import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.KaffeekasseNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.theme.ifAnimationsEnabled
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
 
 
 class KaffeekasseModuleScreenModel private constructor(
@@ -34,10 +34,7 @@ class KaffeekasseModuleScreenModel private constructor(
     }
 }
 
-class KaffeekasseModuleContent(provider: ScreenModelProvider<KaffeekasseModuleScreenModel>) : ScaffoldContentWithModel<KaffeekasseModuleScreenModel>(provider) {
-    @Composable
-    override fun Content(navigator: Navigator) { }
-
+class KaffeekasseModuleContent(provider: ScreenModelProvider<KaffeekasseModuleScreenModel>) : AppScaffoldContentWithModel<KaffeekasseModuleScreenModel>(provider) {
     @Composable
     override fun TopAppBarNavigationIcon(navigator: Navigator) {
         CartEmptyNavigation(model, backNavigationHandler(navigator))
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/ManualBill.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/ManualBill.kt
index 3b6ff39d4e445c499d6db7140b47952507332b03..bbdd9751f76d847c1931e7b3767603b721e56d12 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/ManualBill.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/ManualBill.kt
@@ -36,10 +36,10 @@ import androidx.compose.ui.unit.dp
 import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.model.screenModelScope
 import cafe.adriel.voyager.navigator.Navigator
-import net.novagamestudios.common_utils.Logger
 import net.novagamestudios.common_utils.compose.components.CircularProgressIndicator
 import net.novagamestudios.common_utils.compose.components.RowCenter
 import net.novagamestudios.common_utils.compose.tabIndicatorOffset
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.kaffeekasse.KaffeekasseModule.Companion.kaffeekasseCartProvider
@@ -59,16 +59,16 @@ import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.CheckoutState
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.CustomItems
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.CustomItemsState
 import net.novagamestudios.kaffeekasse.ui.login.LogoutTopBarAction
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.AppSubpageTitle
 import net.novagamestudios.kaffeekasse.ui.navigation.KaffeekasseNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.util.FailureRetryScreen
 import net.novagamestudios.kaffeekasse.ui.util.RichDataContent
 import net.novagamestudios.kaffeekasse.ui.util.TopBarSearchAction
 import net.novagamestudios.kaffeekasse.ui.util.TopBarSearchField
 import net.novagamestudios.kaffeekasse.ui.util.TopBarSearchFieldState
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler.Companion.then
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler.Companion.then
 import net.novagamestudios.kaffeekasse.util.richdata.collectAsRichStateHere
 
 
@@ -191,12 +191,7 @@ class ManualBillScreenModel private constructor(
 }
 
 
-class ManualBillContent(provider: ScreenModelProvider<ManualBillScreenModel>) : ScaffoldContentWithModel<ManualBillScreenModel>(provider) {
-    @Composable
-    override fun Content(navigator: Navigator) {
-        DynamicManualBill(model)
-    }
-
+class ManualBillContent(provider: ScreenModelProvider<ManualBillScreenModel>) : AppScaffoldContentWithModel<ManualBillScreenModel>(provider) {
     @Composable
     override fun TopAppBarTitle(navigator: Navigator) {
         RowCenter {
@@ -225,7 +220,7 @@ class ManualBillContent(provider: ScreenModelProvider<ManualBillScreenModel>) :
 
 
 @Composable
-private fun DynamicManualBill(
+fun DynamicManualBill(
     model: ManualBillScreenModel,
     modifier: Modifier = Modifier,
     postTabsContent: (@Composable () -> Unit)? = null
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/Transactions.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/Transactions.kt
index 1cfed98f088c0c527e2230c983a34485fc92aec5..7edd7f217b28e879caed126757bea36274c2c02d 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/Transactions.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/Transactions.kt
@@ -39,7 +39,7 @@ import androidx.compose.ui.unit.dp
 import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.model.screenModelScope
 import cafe.adriel.voyager.navigator.Navigator
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.kaffeekasse.App
@@ -50,13 +50,13 @@ import net.novagamestudios.kaffeekasse.model.session.Session
 import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
 import net.novagamestudios.kaffeekasse.repositories.i11.KaffeekasseRepository
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.cards.CategoryIcon
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.AppSubpageTitle
 import net.novagamestudios.kaffeekasse.ui.navigation.KaffeekasseNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.util.FailureRetryScreen
 import net.novagamestudios.kaffeekasse.ui.util.PullToRefreshBox
 import net.novagamestudios.kaffeekasse.ui.util.RichDataContent
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
 import net.novagamestudios.kaffeekasse.ui.util.openInBrowser
 import net.novagamestudios.kaffeekasse.util.richdata.collectAsRichStateHere
 import java.time.LocalDate
@@ -87,10 +87,7 @@ class TransactionsScreenModel private constructor(
 }
 
 
-class TransactionsContent(provider: ScreenModelProvider<TransactionsScreenModel>) : ScaffoldContentWithModel<TransactionsScreenModel>(provider) {
-    @Composable
-    override fun Content(navigator: Navigator) = Transactions(model)
-
+class TransactionsContent(provider: ScreenModelProvider<TransactionsScreenModel>) : AppScaffoldContentWithModel<TransactionsScreenModel>(provider) {
     @Composable
     override fun TopAppBarTitle(navigator: Navigator) {
         AppSubpageTitle("Übersicht")
@@ -114,7 +111,7 @@ class TransactionsContent(provider: ScreenModelProvider<TransactionsScreenModel>
 
 
 @Composable
-private fun Transactions(
+fun Transactions(
     model: TransactionsScreenModel,
     modifier: Modifier = Modifier
 ) = PullToRefreshBox(
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/TransactionsCharts.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/TransactionsCharts.kt
index 723d84287d440a666a917b020a71699806b6470e..ade4aa7eeb054c6499455ca6c32a9c5a63986ce6 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/TransactionsCharts.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/TransactionsCharts.kt
@@ -76,8 +76,8 @@ import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.supervisorScope
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.debug
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.debug
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.ItemCategory
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Transaction
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.TransactionsChartsState.ChartSettings.TimeFilter
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/CategorizedItems.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/CategorizedItems.kt
index 659dd7650786a1d16ad104e617f266889b3b7e44..e91a08b99a78aac10391df5e4d55fbcf2290d2ea 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/CategorizedItems.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/CategorizedItems.kt
@@ -20,8 +20,8 @@ import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import kotlinx.coroutines.launch
+import net.novagamestudios.common_utils.android.toastShort
 import net.novagamestudios.common_utils.compose.state.MutableDataStoreState
-import net.novagamestudios.common_utils.toastShort
 import net.novagamestudios.kaffeekasse.App.Companion.settings
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Cart
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Item
@@ -32,7 +32,7 @@ import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.cards.BasicCard
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.cards.CategoryCard
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.cards.LazyBasicCardGrid
 import net.novagamestudios.kaffeekasse.ui.theme.ifAnimationsEnabled
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
 
 class CategorizedItemsState(
     items: Iterable<Item>
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/Checkout.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/Checkout.kt
index 1483f64555eece1d4bffb9962af76fa52abc1d96..b809438ec9c1c879c17b9534b72f7fb34e071881 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/Checkout.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/Checkout.kt
@@ -48,15 +48,15 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.launch
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.compose.Toasts
+import net.novagamestudios.common_utils.compose.ToastsState
 import net.novagamestudios.common_utils.compose.components.BoxCenter
 import net.novagamestudios.common_utils.compose.components.CircularLoadingBox
-import net.novagamestudios.common_utils.compose.components.Toasts
-import net.novagamestudios.common_utils.compose.components.ToastsState
 import net.novagamestudios.common_utils.compose.components.TransparentListItem
 import net.novagamestudios.common_utils.compose.maskedCircleIcon
 import net.novagamestudios.common_utils.compose.state.ReentrantActionState
-import net.novagamestudios.common_utils.warn
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.warn
 import net.novagamestudios.kaffeekasse.KaffeekasseModule.Companion.kaffeekasseCartProvider
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.APIPurchaseAccount
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Cart
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/CustomItems.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/CustomItems.kt
index a0b715c75ae3fa8295808fbc807a4d2a308e21bb..6b65b4bd1a7a06b136187a3b011214c941f9eb3c 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/CustomItems.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/CustomItems.kt
@@ -31,11 +31,11 @@ import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.common_utils.compose.DashedShape
 import net.novagamestudios.common_utils.compose.components.ColumnCenter
 import net.novagamestudios.common_utils.compose.state.ReentrantActionState
-import net.novagamestudios.common_utils.verbose
+import net.novagamestudios.common_utils.logging.verbose
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Item
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.MutableCart
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Transaction
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/cards/Item.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/cards/Item.kt
index 29429ce1f8ed61d1dae169c7ceb2d99c25b72b4a..509a4f4bc7ff87486e1267137a58f0de892b969d 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/cards/Item.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/kaffeekasse/components/cards/Item.kt
@@ -52,11 +52,11 @@ import coil.compose.SubcomposeAsyncImage
 import coil.request.ImageRequest
 import io.ktor.http.URLBuilder
 import io.ktor.http.URLProtocol
-import net.novagamestudios.common_utils.LocalLogger
 import net.novagamestudios.common_utils.compose.components.BoxCenter
 import net.novagamestudios.common_utils.compose.maskedCircleIcon
-import net.novagamestudios.common_utils.info
-import net.novagamestudios.common_utils.warn
+import net.novagamestudios.common_utils.logging.LocalLogger
+import net.novagamestudios.common_utils.logging.info
+import net.novagamestudios.common_utils.logging.warn
 import net.novagamestudios.kaffeekasse.App
 import net.novagamestudios.kaffeekasse.app
 import net.novagamestudios.kaffeekasse.model.kaffeekasse.Item
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/Login.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/Login.kt
index 9c7ae038eaf50451c33145ef3c259a22302ecb5b..e88727d079100729346c3e7977b521d6e6857392 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/Login.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/Login.kt
@@ -1,23 +1,21 @@
 package net.novagamestudios.kaffeekasse.ui.login
 
-import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.Logout
-import androidx.compose.material3.CircularProgressIndicator
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.ui.Modifier
 import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.model.screenModelScope
 import cafe.adriel.voyager.navigator.Navigator
 import kotlinx.coroutines.launch
-import net.novagamestudios.common_utils.Logger
-import net.novagamestudios.common_utils.compose.components.BoxCenter
 import net.novagamestudios.common_utils.compose.components.CircularLoadingBox
+import net.novagamestudios.common_utils.compose.components.scaffold.currentScaffoldContent
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.common_utils.voyager.model.collectAsStateHere
@@ -26,10 +24,8 @@ import net.novagamestudios.kaffeekasse.model.session.Session
 import net.novagamestudios.kaffeekasse.repositories.LoginRepository
 import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
 import net.novagamestudios.kaffeekasse.repositories.i11.PortalRepository
-import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldNavigatorDefaultContent
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.LoginNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
 
 
 class LoginScreenModel private constructor(
@@ -52,27 +48,18 @@ class LoginScreenModel private constructor(
     }
 }
 
-class LoginContent(provider: ScreenModelProvider<LoginScreenModel>) : ScaffoldContentWithModel<LoginScreenModel>(provider) {
+class LoginContent(provider: ScreenModelProvider<LoginScreenModel>) : AppScaffoldContentWithModel<LoginScreenModel>(provider) {
     @Composable
-    override fun Content(navigator: Navigator) {
-        if (model.isLoading) {
-            BoxCenter(Modifier.fillMaxSize()) {
-                CircularProgressIndicator()
-            }
-        } else {
-            navigator.AppScaffoldNavigatorDefaultContent("login-navigator-content")
-        }
-        Additional(model, navigator)
-    }
+    override fun TopAppBarNavigationIcon(navigator: Navigator) { }
 
     @Composable
     override fun backNavigationHandler(navigator: Navigator): BackNavigationHandler {
-        return default(navigator) { backNavigationHandler(it) } ?: BackNavigationHandler.default()
+        return navigator.currentScaffoldContent()?.backNavigationHandler(navigator) ?: BackNavigationHandler.default()
     }
 }
 
 @Composable
-private fun Additional(model: LoginScreenModel, navigator: Navigator) {
+fun LoginAdditional(model: LoginScreenModel, navigator: Navigator) {
     when (navigator.lastItem) {
         is LoginNavigation.FormScreen -> {
             val session = model.session
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginDevice.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginDevice.kt
index a09daf2db38259b36587d8d57a0fce7f030b0fbd..e37cd50dd913eb3317271762884988fcf1e904ce 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginDevice.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginDevice.kt
@@ -35,11 +35,11 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 import net.novagamestudios.common_utils.compose.components.CircularLoadingBox
 import net.novagamestudios.common_utils.compose.state.collectAsStateIn
+import net.novagamestudios.common_utils.voyager.nearestScreen
 import net.novagamestudios.kaffeekasse.model.credentials.DeviceCredentials
 import net.novagamestudios.kaffeekasse.model.credentials.isValid
 import net.novagamestudios.kaffeekasse.repositories.LoginRepository
 import net.novagamestudios.kaffeekasse.ui.navigation.LoginNavigation
-import net.novagamestudios.kaffeekasse.ui.util.navigation.nearestScreen
 import net.novagamestudios.kaffeekasse.ui.util.rememberSerializableState
 
 
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginForm.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginForm.kt
index bc4608226baf54bf66ce819d200c583f49e3f9b3..ade5e1c1041096710363351b0716016e5bcf05cf 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginForm.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/LoginForm.kt
@@ -49,8 +49,8 @@ import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.model.screenModelScope
 import cafe.adriel.voyager.navigator.Navigator
 import kotlinx.coroutines.launch
-import net.novagamestudios.common_utils.Logger
 import net.novagamestudios.common_utils.compose.components.BoxCenter
+import net.novagamestudios.common_utils.logging.Logger
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.common_utils.voyager.model.collectAsStateHere
@@ -63,8 +63,8 @@ import net.novagamestudios.kaffeekasse.repositories.i11.PortalRepository
 import net.novagamestudios.kaffeekasse.ui.AppInfoTopBarAction
 import net.novagamestudios.kaffeekasse.ui.FullscreenIconButton
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.FailureRetryScreen
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.LoginNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.util.autofill
 import net.novagamestudios.kaffeekasse.ui.util.rememberSerializableState
 
@@ -106,10 +106,7 @@ class LoginFormScreenModel private constructor(
     }
 }
 
-class LoginFormContent(provider: ScreenModelProvider<LoginFormScreenModel>) : ScaffoldContentWithModel<LoginFormScreenModel>(provider) {
-    @Composable
-    override fun Content(navigator: Navigator) = LoginForm(model)
-
+class LoginFormContent(provider: ScreenModelProvider<LoginFormScreenModel>) : AppScaffoldContentWithModel<LoginFormScreenModel>(provider) {
     @Composable
     override fun TopAppBarActions(navigator: Navigator) {
         AppInfoTopBarAction()
@@ -119,7 +116,7 @@ class LoginFormContent(provider: ScreenModelProvider<LoginFormScreenModel>) : Sc
 }
 
 @Composable
-private fun LoginForm(
+fun LoginForm(
     model: LoginFormScreenModel,
     modifier: Modifier = Modifier
 ) = BoxCenter(modifier) {
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/UserSelection.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/UserSelection.kt
index a97127a2321c196d77a1ab6210b29bd9af74bf11..14275b2c9e8653e7fecbeef68d663ade8e10b9c4 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/UserSelection.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/login/UserSelection.kt
@@ -67,15 +67,15 @@ import cafe.adriel.voyager.navigator.Navigator
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.launch
-import net.novagamestudios.common_utils.Logger
+import net.novagamestudios.common_utils.compose.Toasts
+import net.novagamestudios.common_utils.compose.ToastsState
 import net.novagamestudios.common_utils.compose.components.LinearProgressIndicator
 import net.novagamestudios.common_utils.compose.components.RowCenter
-import net.novagamestudios.common_utils.compose.components.Toasts
-import net.novagamestudios.common_utils.compose.components.ToastsState
 import net.novagamestudios.common_utils.compose.state.rememberDerivedStateOf
+import net.novagamestudios.common_utils.logging.Logger
+import net.novagamestudios.common_utils.logging.warn
 import net.novagamestudios.common_utils.voyager.model.ScreenModelFactory
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
-import net.novagamestudios.common_utils.warn
 import net.novagamestudios.kaffeekasse.App
 import net.novagamestudios.kaffeekasse.R
 import net.novagamestudios.kaffeekasse.api.kaffeekasse.model.BasicUserInfo
@@ -88,8 +88,8 @@ import net.novagamestudios.kaffeekasse.repositories.i11.KaffeekasseRepository
 import net.novagamestudios.kaffeekasse.ui.AppInfoTopBarAction
 import net.novagamestudios.kaffeekasse.ui.FullscreenIconButton
 import net.novagamestudios.kaffeekasse.ui.handleSession
+import net.novagamestudios.kaffeekasse.ui.navigation.AppScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.navigation.LoginNavigation
-import net.novagamestudios.kaffeekasse.ui.navigation.ScaffoldContentWithModel
 import net.novagamestudios.kaffeekasse.ui.util.AlphabetSelectionChar
 import net.novagamestudios.kaffeekasse.ui.util.FailureRetryScreen
 import net.novagamestudios.kaffeekasse.ui.util.HorizontalSelectionBar
@@ -99,7 +99,7 @@ import net.novagamestudios.kaffeekasse.ui.util.TopBarSearchAction
 import net.novagamestudios.kaffeekasse.ui.util.TopBarSearchField
 import net.novagamestudios.kaffeekasse.ui.util.TopBarSearchFieldState
 import net.novagamestudios.kaffeekasse.ui.util.VerticalSelectionBar
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
 import net.novagamestudios.kaffeekasse.ui.util.pullToRefresh
 import net.novagamestudios.kaffeekasse.util.richdata.asRichDataFlow
 import net.novagamestudios.kaffeekasse.util.richdata.combineRich
@@ -179,10 +179,7 @@ class UserSelectionScreenModel private constructor(
 }
 
 
-class UserSelectionContent(provider: ScreenModelProvider<UserSelectionScreenModel>) : ScaffoldContentWithModel<UserSelectionScreenModel>(provider) {
-    @Composable
-    override fun Content(navigator: Navigator) = UserSelection(model)
-
+class UserSelectionContent(provider: ScreenModelProvider<UserSelectionScreenModel>) : AppScaffoldContentWithModel<UserSelectionScreenModel>(provider) {
     @Composable
     override fun TopAppBarTitle(navigator: Navigator) {
         Row(verticalAlignment = Alignment.CenterVertically) {
@@ -217,7 +214,7 @@ class UserSelectionContent(provider: ScreenModelProvider<UserSelectionScreenMode
 
 
 @Composable
-private fun UserSelection(
+fun UserSelection(
     model: UserSelectionScreenModel,
     modifier: Modifier = Modifier
 ) {
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/AppScaffold.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/AppScaffold.kt
index 23e6682733a2d3796279f02285f15e483e7dd94c..0b18171354edbf72b6501aab9c127e73eeabe64a 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/AppScaffold.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/AppScaffold.kt
@@ -1,66 +1,45 @@
 package net.novagamestudios.kaffeekasse.ui.navigation
 
-import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.expandHorizontally
 import androidx.compose.animation.shrinkHorizontally
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.ArrowBack
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Scaffold
 import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBar
 import androidx.compose.material3.TopAppBarDefaults
 import androidx.compose.material3.TopAppBarScrollBehavior
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.unit.dp
+import cafe.adriel.voyager.core.model.ScreenModel
 import cafe.adriel.voyager.core.screen.Screen
 import cafe.adriel.voyager.navigator.Navigator
+import net.novagamestudios.common_utils.compose.components.scaffold.TopAppBar
+import net.novagamestudios.common_utils.compose.components.scaffold.TopAppBarContent
+import net.novagamestudios.common_utils.compose.nestedScroll
+import net.novagamestudios.common_utils.voyager.AnimatedBackNavigationIcon
+import net.novagamestudios.common_utils.voyager.debugNavigation
+import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.kaffeekasse.CrashHandling
 import net.novagamestudios.kaffeekasse.ui.theme.LocalAnimationSwitch
 import net.novagamestudios.kaffeekasse.ui.theme.ifAnimationsEnabled
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
-import net.novagamestudios.kaffeekasse.ui.util.navigation.debugNavigation
 
 
-@Composable
-fun AppScaffoldNavigator(
-    key: String,
-    initialRoute: List<Screen>,
-    scaffoldContent: ScaffoldContent,
-    title: @Composable (Navigator) -> Unit = { scaffoldContent.TopAppBarTitle(it) },
-    navigationIcon: @Composable (Navigator) -> Unit = { scaffoldContent.TopAppBarNavigationIcon(it) },
-    actions: @Composable RowScope.(Navigator) -> Unit = { scaffoldContent.TopAppBarActions(it) },
-    topAppBarScrollBehavior: TopAppBarScrollBehavior? = TopAppBarDefaults.enterAlwaysScrollBehavior(),
-    backNavigationHandlerProvider: @Composable (Navigator) -> BackNavigationHandler = { scaffoldContent.backNavigationHandler(it) },
-    content: @Composable Navigator.() -> Unit = { AppScaffoldNavigatorDefaultContent("$key-content") }
-) = AppScaffoldNavigator(
-    key = key,
-    initialRoute = initialRoute,
-    title = title,
-    navigationIcon = navigationIcon,
-    actions = actions,
-    topAppBarScrollBehavior = topAppBarScrollBehavior,
-    backNavigationHandlerProvider = backNavigationHandlerProvider,
-    content = content
-)
+interface AppScaffoldContent : TopAppBarContent
+
+abstract class AppScaffoldContentWithModel<out T : ScreenModel>(
+    provider: ScreenModelProvider<T>
+) : AppScaffoldContent, ScreenModelProvider<T> by provider
+
 
 @Composable
 fun AppScaffoldNavigator(
     key: String,
     initialRoute: List<Screen>,
-    title: @Composable (Navigator) -> Unit = { },
-    navigationIcon: @Composable (Navigator) -> Unit = { },
-    actions: @Composable RowScope.(Navigator) -> Unit = { },
-    topAppBarScrollBehavior: TopAppBarScrollBehavior? = TopAppBarDefaults.enterAlwaysScrollBehavior(),
-    backNavigationHandlerProvider: @Composable (Navigator) -> BackNavigationHandler = { BackNavigationHandler.default() },
+    scaffoldContent: AppScaffoldContent,
+    topAppBarScrollBehavior: TopAppBarScrollBehavior? = TopAppBarDefaults.enterAlwaysScrollBehavior().takeIf { LocalAnimationSwitch.current },
     content: @Composable Navigator.() -> Unit = { AppScaffoldNavigatorDefaultContent("$key-content") }
 ) = Navigator(
     screens = initialRoute,
@@ -69,23 +48,35 @@ fun AppScaffoldNavigator(
 ) { navigator ->
     debugNavigation()
     CrashHandling.updateNavigation(navigator)
-    val backNavigationHandler = backNavigationHandlerProvider(navigator)
-    AppScaffold(
+    Scaffold(
+        Modifier.nestedScroll(topAppBarScrollBehavior?.nestedScrollConnection),
         topBar = {
-            AppTopBar(
-                title = { title(navigator) },
-                navigationIcon = {
-                    DefaultBackNavigation(backNavigationHandler)
-                    navigationIcon(navigator)
+            TopAppBar(
+                content = scaffoldContent,
+                navigator = navigator,
+                backNavigationIcon = {
+                    AnimatedBackNavigationIcon(
+                        handler = it,
+                        enter = expandHorizontally().ifAnimationsEnabled(),
+                        exit = shrinkHorizontally().ifAnimationsEnabled()
+                    )
                 },
-                actions = { actions(navigator) },
+                colors = TopAppBarDefaults.topAppBarColors(
+                    scrolledContainerColor = MaterialTheme.colorScheme.surface
+                ),
                 scrollBehavior = topAppBarScrollBehavior
             )
-        },
-        topAppBarScrollBehavior = topAppBarScrollBehavior
-    ) {
-        navigator.content()
-        backNavigationHandler.BackHandler()
+        }
+    ) { paddingValues ->
+        Box(
+            Modifier
+                .padding(paddingValues)
+                .fillMaxSize(),
+            propagateMinConstraints = true
+        ) {
+            navigator.content()
+            scaffoldContent.backNavigationHandler(navigator).BackHandler()
+        }
     }
 }
 
@@ -98,61 +89,6 @@ fun Navigator.AppScaffoldNavigatorDefaultContent(
     }
 }
 
-@Composable
-private fun AppScaffold(
-    modifier: Modifier = Modifier,
-    topBar: @Composable () -> Unit = { },
-    topAppBarScrollBehavior: TopAppBarScrollBehavior? = null,
-    content: @Composable () -> Unit
-) = Scaffold(
-    modifier.run {
-        if (topAppBarScrollBehavior == null || !LocalAnimationSwitch.current) this
-        else nestedScroll(topAppBarScrollBehavior.nestedScrollConnection)
-    },
-    topBar = topBar
-) { paddingValues ->
-    Box(
-        Modifier
-            .padding(paddingValues)
-            .fillMaxSize(),
-        propagateMinConstraints = true
-    ) {
-        content()
-    }
-}
-
-@Composable
-fun AppTopBar(
-    title: @Composable () -> Unit,
-    modifier: Modifier = Modifier,
-    navigationIcon: @Composable () -> Unit = {},
-    actions: @Composable RowScope.() -> Unit = {},
-    scrollBehavior: TopAppBarScrollBehavior? = null
-) = TopAppBar(
-    title = { title() },
-    modifier = modifier,
-    navigationIcon = { navigationIcon() },
-    actions = { actions() },
-    colors = TopAppBarDefaults.topAppBarColors(
-        scrolledContainerColor = MaterialTheme.colorScheme.surface
-    ),
-    scrollBehavior = scrollBehavior.takeIf { LocalAnimationSwitch.current }
-)
 
 @Composable
 fun AppSubpageTitle(text: String) = Text(text, Modifier.padding(start = 8.dp))
-
-@Composable
-fun DefaultBackNavigation(
-    handler: BackNavigationHandler
-) {
-    AnimatedVisibility(
-        visible = handler.canNavigateBack(),
-        enter = expandHorizontally().ifAnimationsEnabled(),
-        exit = shrinkHorizontally().ifAnimationsEnabled()
-    ) {
-        IconButton(onClick = { handler.onNavigateBack() }) {
-            Icon(Icons.AutoMirrored.Filled.ArrowBack, "Back")
-        }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/AppScreens.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/AppScreens.kt
index 0275f8820f7ea20e1e86c782a2860a5f84db8640..a3da37d2e9d46fb81e54644b99f5d0985e15786c 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/AppScreens.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/AppScreens.kt
@@ -2,6 +2,7 @@ package net.novagamestudios.kaffeekasse.ui.navigation
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.CircularProgressIndicator
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -11,6 +12,8 @@ import cafe.adriel.voyager.core.screen.Screen
 import cafe.adriel.voyager.navigator.tab.Tab
 import cafe.adriel.voyager.navigator.tab.TabNavigator
 import cafe.adriel.voyager.navigator.tab.TabOptions
+import net.novagamestudios.common_utils.compose.components.BoxCenter
+import net.novagamestudios.common_utils.compose.components.scaffold.ScaffoldContentProvider
 import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
 import net.novagamestudios.common_utils.voyager.model.getValue
 import net.novagamestudios.common_utils.voyager.screen.ScreenWithModel
@@ -20,27 +23,34 @@ import net.novagamestudios.kaffeekasse.model.session.deviceOrNull
 import net.novagamestudios.kaffeekasse.ui.AppModulesScreenModel
 import net.novagamestudios.kaffeekasse.ui.hiwi_tracker.HiwiTrackerModuleContent
 import net.novagamestudios.kaffeekasse.ui.hiwi_tracker.HiwiTrackerModuleScreenModel
+import net.novagamestudios.kaffeekasse.ui.hiwi_tracker.Overview
 import net.novagamestudios.kaffeekasse.ui.hiwi_tracker.OverviewContent
 import net.novagamestudios.kaffeekasse.ui.hiwi_tracker.OverviewScreenModel
+import net.novagamestudios.kaffeekasse.ui.kaffeekasse.Account
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.AccountContent
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.AccountScreenModel
+import net.novagamestudios.kaffeekasse.ui.kaffeekasse.DynamicManualBill
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.KaffeekasseModuleContent
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.KaffeekasseModuleScreenModel
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.ManualBillContent
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.ManualBillScreenModel
+import net.novagamestudios.kaffeekasse.ui.kaffeekasse.Transactions
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.TransactionsContent
 import net.novagamestudios.kaffeekasse.ui.kaffeekasse.TransactionsScreenModel
+import net.novagamestudios.kaffeekasse.ui.login.LoginAdditional
 import net.novagamestudios.kaffeekasse.ui.login.LoginContent
+import net.novagamestudios.kaffeekasse.ui.login.LoginForm
 import net.novagamestudios.kaffeekasse.ui.login.LoginFormContent
 import net.novagamestudios.kaffeekasse.ui.login.LoginFormScreenModel
 import net.novagamestudios.kaffeekasse.ui.login.LoginScreenModel
+import net.novagamestudios.kaffeekasse.ui.login.UserSelection
 import net.novagamestudios.kaffeekasse.ui.login.UserSelectionContent
 import net.novagamestudios.kaffeekasse.ui.login.UserSelectionScreenModel
 
 
 sealed interface LoginNavigation {
 
-    companion object : LoginNavigation, ScreenWithModel<LoginScreenModel>, ScaffoldScreen {
+    companion object : LoginNavigation, ScreenWithModel<LoginScreenModel>, ScaffoldContentProvider {
         @get:Composable override val model by LoginScreenModel
         private val initialScreen: Screen @Composable get() = model
             .session
@@ -51,25 +61,34 @@ sealed interface LoginNavigation {
         @Composable override fun Content() = AppScaffoldNavigator(
             key = "login-navigator",
             initialRoute = listOf(initialScreen),
-            title = { scaffoldContent.TopAppBarTitle(it) },
-            actions = { scaffoldContent.TopAppBarActions(it) },
+            scaffoldContent = scaffoldContent,
             topAppBarScrollBehavior = null,
-            content = { scaffoldContent.Content(this) },
-            backNavigationHandlerProvider = { scaffoldContent.backNavigationHandler(it) }
+            content = {
+                if (model.isLoading) {
+                    BoxCenter(Modifier.fillMaxSize()) {
+                        CircularProgressIndicator()
+                    }
+                } else {
+                    AppScaffoldNavigatorDefaultContent("login-navigator-content")
+                }
+                LoginAdditional(model, this)
+            }
         )
     }
 
-    data object FormScreen : LoginNavigation, ScreenWithModel<LoginFormScreenModel>, ScaffoldScreen {
+    data object FormScreen : LoginNavigation, ScreenWithModel<LoginFormScreenModel>, ScaffoldContentProvider {
         private fun readResolve(): Any = FormScreen
         @get:Composable override val model by LoginFormScreenModel
         override val scaffoldContent = LoginFormContent(this)
+        @Composable override fun Content() = LoginForm(model)
     }
 
     data class UserSelectionScreen(
         val device: Device
-    ) : LoginNavigation, ScreenWithModel<UserSelectionScreenModel>, ScaffoldScreen {
+    ) : LoginNavigation, ScreenWithModel<UserSelectionScreenModel>, ScaffoldContentProvider {
         @get:Composable override val model by UserSelectionScreenModel
         override val scaffoldContent = UserSelectionContent(this)
+        @Composable override fun Content() = UserSelection(model)
     }
 }
 
@@ -110,7 +129,7 @@ sealed interface KaffeekasseNavigation {
 
     data class Tab(
         override val session: Session.WithRealUser
-    ) : ModuleTab, KaffeekasseNavigation, ScreenModelProvider<KaffeekasseModuleScreenModel>, ScaffoldScreen {
+    ) : ModuleTab, KaffeekasseNavigation, ScreenModelProvider<KaffeekasseModuleScreenModel>, ScaffoldContentProvider {
         @get:Composable override val model by KaffeekasseModuleScreenModel
         override val scaffoldContent = KaffeekasseModuleContent(this)
         @Composable override fun Content() = AppScaffoldNavigator(
@@ -123,23 +142,26 @@ sealed interface KaffeekasseNavigation {
 
     data class ManualBillScreen(
         val session: Session.WithRealUser
-    ) : KaffeekasseNavigation, ScreenWithModel<ManualBillScreenModel>, ScaffoldScreen {
+    ) : KaffeekasseNavigation, ScreenWithModel<ManualBillScreenModel>, ScaffoldContentProvider {
         @get:Composable override val model by ManualBillScreenModel
         override val scaffoldContent = ManualBillContent(this)
+        @Composable override fun Content() = DynamicManualBill(model)
     }
 
     data class AccountScreen(
         val session: Session.WithRealUser
-    ) : KaffeekasseNavigation, ScreenWithModel<AccountScreenModel>, ScaffoldScreen {
+    ) : KaffeekasseNavigation, ScreenWithModel<AccountScreenModel>, ScaffoldContentProvider {
         @get:Composable override val model by AccountScreenModel
         override val scaffoldContent = AccountContent(this)
+        @Composable override fun Content() = Account(model)
     }
 
     data class TransactionsScreen(
         val session: Session.WithRealUser
-    ) : KaffeekasseNavigation, ScreenWithModel<TransactionsScreenModel>, ScaffoldScreen {
+    ) : KaffeekasseNavigation, ScreenWithModel<TransactionsScreenModel>, ScaffoldContentProvider {
         @get:Composable override val model by TransactionsScreenModel
         override val scaffoldContent = TransactionsContent(this)
+        @Composable override fun Content() = Transactions(model)
     }
 }
 
@@ -148,7 +170,7 @@ sealed interface HiwiTrackerNavigation {
 
     data class Tab(
         override val session: Session.WithRealUser
-    ) : ModuleTab, HiwiTrackerNavigation, ScreenModelProvider<HiwiTrackerModuleScreenModel>, ScaffoldScreen {
+    ) : ModuleTab, HiwiTrackerNavigation, ScreenModelProvider<HiwiTrackerModuleScreenModel>, ScaffoldContentProvider {
         @get:Composable override val model by HiwiTrackerModuleScreenModel
         override val scaffoldContent = HiwiTrackerModuleContent(this)
         @Composable override fun Content() = AppScaffoldNavigator(
@@ -161,8 +183,9 @@ sealed interface HiwiTrackerNavigation {
 
     data class OverviewScreen(
         val session: Session.WithRealUser
-    ) : HiwiTrackerNavigation, ScreenWithModel<OverviewScreenModel>, ScaffoldScreen {
+    ) : HiwiTrackerNavigation, ScreenWithModel<OverviewScreenModel>, ScaffoldContentProvider {
         @get:Composable override val model by OverviewScreenModel
         override val scaffoldContent = OverviewContent(this)
+        @Composable override fun Content() = Overview(model)
     }
 }
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/ScaffoldContent.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/ScaffoldContent.kt
deleted file mode 100644
index 4c00c388d4ecdbd72e7d2125c812c5bbca62d39d..0000000000000000000000000000000000000000
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/navigation/ScaffoldContent.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-package net.novagamestudios.kaffeekasse.ui.navigation
-
-import androidx.compose.runtime.Composable
-import cafe.adriel.voyager.core.model.ScreenModel
-import cafe.adriel.voyager.core.screen.Screen
-import cafe.adriel.voyager.navigator.LocalNavigator
-import cafe.adriel.voyager.navigator.Navigator
-import cafe.adriel.voyager.navigator.currentOrThrow
-import net.novagamestudios.common_utils.voyager.model.ScreenModelProvider
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
-import java.io.Serializable
-
-
-abstract class ScaffoldContent : Serializable {
-    @Composable
-    abstract fun Content(navigator: Navigator)
-    @Composable
-    open fun TopAppBarNavigationIcon(navigator: Navigator): Unit = default(navigator) { TopAppBarNavigationIcon(it) } ?: Unit
-    @Composable
-    open fun TopAppBarTitle(navigator: Navigator): Unit = default(navigator) { TopAppBarTitle(it) } ?: Unit
-    @Composable
-    open fun TopAppBarActions(navigator: Navigator): Unit = default(navigator) { TopAppBarActions(it) } ?: Unit
-    @Composable
-    open fun backNavigationHandler(navigator: Navigator): BackNavigationHandler = default(navigator) { backNavigationHandler(it) } ?: BackNavigationHandler.Disabled
-
-    @Composable
-    protected inline fun <R> default(navigator: Navigator, block: ScaffoldContent.(Navigator) -> R): R? {
-        val screen = navigator.lastItem
-        if (screen !is ScaffoldScreen) return null
-        val scaffoldContent = screen.scaffoldContent
-        if (scaffoldContent == this) return null
-        return scaffoldContent.block(navigator)
-    }
-}
-
-abstract class ScaffoldContentWithModel<out T : ScreenModel>(
-    provider: ScreenModelProvider<T>
-) : ScaffoldContent(), ScreenModelProvider<T> by provider
-
-interface ScaffoldScreen : Screen {
-    val scaffoldContent: ScaffoldContent
-    @Composable
-    override fun Content() = scaffoldContent.Content(LocalNavigator.currentOrThrow)
-}
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/Helpers.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/Helpers.kt
index d740b225547a81cf21619448d7864538bf89741c..0b98aa8ed0a4d28422ef1c1a88b0f580ac44c891 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/Helpers.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/Helpers.kt
@@ -21,9 +21,9 @@ import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.TextUnit
 import io.ktor.http.Url
-import net.novagamestudios.common_utils.LoggerForFun
-import net.novagamestudios.common_utils.error
-import net.novagamestudios.common_utils.toastShort
+import net.novagamestudios.common_utils.android.toastShort
+import net.novagamestudios.common_utils.logging.LoggerForFun
+import net.novagamestudios.common_utils.logging.error
 
 
 @Composable
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/TopBarSearchField.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/TopBarSearchField.kt
index 118993cc476965a9dc05d3167bf58f9327350526..6726d62d130ce4317003e7bead6529385db7fc53 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/TopBarSearchField.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/TopBarSearchField.kt
@@ -16,7 +16,7 @@ import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
-import net.novagamestudios.kaffeekasse.ui.util.navigation.BackNavigationHandler
+import net.novagamestudios.common_utils.voyager.BackNavigationHandler
 
 
 class TopBarSearchFieldState {
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/navigation/BackNavigationHandler.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/navigation/BackNavigationHandler.kt
deleted file mode 100644
index eafa69a88d42c823319f547f145fa0ff773e839a..0000000000000000000000000000000000000000
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/navigation/BackNavigationHandler.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package net.novagamestudios.kaffeekasse.ui.util.navigation
-
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
-import cafe.adriel.voyager.navigator.LocalNavigator
-import cafe.adriel.voyager.navigator.Navigator
-import cafe.adriel.voyager.navigator.currentOrThrow
-
-interface BackNavigationHandler {
-
-    /**
-     * @return true if back navigation ui components should be shown.
-     * false does not mean that the navigation can not be handled
-     */
-    fun canNavigateBack(): Boolean
-
-    /**
-     * @return true if the navigation was handled and false if it should be passed on
-     */
-    fun onNavigateBack(): Boolean
-
-    @Composable
-    fun BackHandler() = androidx.activity.compose.BackHandler {
-        onNavigateBack()
-    }
-
-    companion object {
-        val Disabled = object : BackNavigationHandler {
-            override fun canNavigateBack() = false
-            override fun onNavigateBack() = true
-            @Composable
-            override fun BackHandler() { }
-        }
-        fun forNavigator(navigator: Navigator) = object : BackNavigationHandler {
-            override fun canNavigateBack() = navigator.canPop
-            override fun onNavigateBack(): Boolean {
-                navigator.pop()
-                return true
-            }
-        }
-        @Composable
-        fun default(): BackNavigationHandler {
-            val navigator = LocalNavigator.currentOrThrow
-            return remember(navigator) { forNavigator(navigator) }
-        }
-        infix fun BackNavigationHandler?.then(parent: BackNavigationHandler?) = object :
-            BackNavigationHandler {
-            override fun canNavigateBack(): Boolean {
-                return this@then?.canNavigateBack() == true || parent?.canNavigateBack() == true
-            }
-            override fun onNavigateBack(): Boolean {
-                return this@then?.onNavigateBack() == true || parent?.onNavigateBack() == true
-            }
-        }
-
-        fun derivedOf(
-            canNavigateBack: () -> Boolean,
-            onNavigateBack: () -> Boolean
-        ) = object : BackNavigationHandler {
-            private val _canNavigateBack by derivedStateOf(canNavigateBack)
-            override fun canNavigateBack() = _canNavigateBack
-            override fun onNavigateBack() = onNavigateBack()
-        }
-    }
-}
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/navigation/Navigation.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/navigation/Navigation.kt
deleted file mode 100644
index 2aabdf67219712f62d8bc412da1c89b816902170..0000000000000000000000000000000000000000
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/ui/util/navigation/Navigation.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package net.novagamestudios.kaffeekasse.ui.util.navigation
-
-import androidx.compose.runtime.Composable
-import cafe.adriel.voyager.core.annotation.InternalVoyagerApi
-import cafe.adriel.voyager.core.screen.Screen
-import cafe.adriel.voyager.navigator.LocalNavigator
-import cafe.adriel.voyager.navigator.Navigator
-import cafe.adriel.voyager.navigator.currentOrThrow
-import net.novagamestudios.common_utils.debug
-
-@OptIn(InternalVoyagerApi::class)
-@Composable
-fun debugNavigation() {
-    fun StringBuilder.addNavigator(navigator: Navigator) {
-        navigator.parent?.let { addNavigator(it) }
-        val indent = "  ".repeat(navigator.level)
-        appendLine("${indent}Navigator: ${navigator.key}")
-        navigator.items.forEach { screen ->
-            appendLine("${indent}- ${screen.key}: ${screen::class.qualifiedName}")
-        }
-    }
-    val navigator = LocalNavigator.currentOrThrow
-    val result = StringBuilder().apply {
-        addNavigator(navigator)
-    }.toString()
-    debug { result }
-}
-
-tailrec fun Navigator.findNavigatorOrNull(predicate: (Navigator) -> Boolean): Navigator? {
-    if (predicate(this)) return this
-    return parent?.findNavigatorOrNull(predicate)
-}
-
-@OptIn(InternalVoyagerApi::class)
-fun Navigator.requireWithKey(key: String) = findNavigatorOrNull { it.key == key }
-    ?: error("Navigator with key '$key' not found")
-
-
-@Composable
-inline fun <reified T : Screen> nearestScreenOrNull(): T? {
-    var navigator = LocalNavigator.current
-    while (navigator != null) {
-        val screen = navigator.items.filterIsInstance<T>().lastOrNull()
-        if (screen != null) return screen
-        navigator = navigator.parent
-    }
-    return null
-}
-
-@Composable
-inline fun <reified T : Screen> nearestScreen(): T {
-    return nearestScreenOrNull() ?: error("No screen of type ${T::class.qualifiedName} found")
-}
-
-@Suppress("RecursivePropertyAccessor")
-val Navigator.root: Navigator get() = parent?.root ?: this
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/util/JSParser.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/util/JSParser.kt
deleted file mode 100644
index 8c8b1e0179b9da05437b5579f7953240b9617ba9..0000000000000000000000000000000000000000
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/util/JSParser.kt
+++ /dev/null
@@ -1,198 +0,0 @@
-package net.novagamestudios.kaffeekasse.util
-
-import kotlinx.serialization.ExperimentalSerializationApi
-import kotlinx.serialization.json.JsonArray
-import kotlinx.serialization.json.JsonElement
-import kotlinx.serialization.json.JsonObject
-import kotlinx.serialization.json.JsonPrimitive
-
-
-private data class Cursor(
-    val data: String,
-    private var i: Int
-) {
-    fun peek(): Char {
-        if (!hasNext()) throw Exception("Unexpected end of data")
-        return data[i]
-    }
-    fun next(): Char {
-        val char = peek()
-        i++
-        return char
-    }
-    fun hasNext(): Boolean = i < data.length
-
-    fun peek(n: Int): String? {
-        if (i + n > data.length) return null
-        return data.substring(i, i + n)
-    }
-
-
-    fun whitespace() {
-        while (hasNext() && peek().isWhitespace()) next()
-    }
-
-    inline fun nextWhile(crossinline predicate: (Char) -> Boolean) = sequence {
-        while (hasNext()) {
-            if (!predicate(peek())) break
-            yield(next())
-        }
-    }
-
-    fun expect(expect: Char) {
-        val actual = next()
-        if (actual != expect) throw Exception("Expected '$expect' but got '$actual' at ${i - 1}")
-    }
-    fun expect(expect: Set<Char>): Char {
-        val actual = next()
-        if (actual !in expect) throw Exception(
-            "Expected one of ${expect.joinToString(separator = ", ") { "'$it'" }} but got '$actual' at $i"
-        )
-        return actual
-    }
-    fun peekExpect(expect: Set<Char>): Char {
-        val actual = peek()
-        if (actual !in expect) throw Exception(
-            "Expected one of ${expect.joinToString(separator = ", ") { "'$it'" }} but got '$actual' at $i"
-        )
-        return actual
-    }
-
-    fun tryNext(str: String): Boolean {
-        if (i + str.length > data.length) return false
-        if (data.substring(i, i + str.length) != str) return false
-        i += str.length
-        return true
-    }
-
-    override fun toString(): String {
-        val window = 20
-        return data.substring((i - window / 2)..<i) + "|" + data.substring(i..<(i + window / 2))
-    }
-}
-
-private fun Cursor.parseJSArray(): JsonArray {
-    whitespace()
-    expect('[')
-    whitespace()
-    if (peek() == ']') return JsonArray(emptyList())
-    val content = mutableListOf<JsonElement>()
-    while (true) {
-        content += parseJSElement()
-        whitespace()
-        if (expect(setOf(',', ']')) == ']') break
-    }
-    return JsonArray(content)
-}
-
-private fun Cursor.parseJSObject(): JsonObject {
-    whitespace()
-    expect('{')
-    whitespace()
-    if (peek() == '}') return JsonObject(emptyMap())
-    val content = mutableMapOf<String, JsonElement>()
-    while (true) {
-        whitespace()
-        val key = tryParseJSIdentifier()
-            ?: tryParseJSString()
-            ?: tryParseJSNumber()?.toString()
-            ?: throw Exception("Invalid key starting with '${peek()}'")
-        if (key in content) throw Exception("Duplicate key \"$key\"")
-        whitespace()
-        expect(':')
-        whitespace()
-        content[key] = parseJSElement()
-        whitespace()
-        if (expect(setOf(',', '}')) == '}') break
-    }
-    return JsonObject(content)
-}
-
-
-private fun Cursor.tryParseJSBoolean(): Boolean? {
-    if (tryNext("true")) return true
-    if (tryNext("false")) return false
-    return null
-}
-
-
-private fun Cursor.tryParseJSIdentifier(): String? {
-    if (peek().let { it.isLetter() || it == '$' || it == '_' }) {
-        return nextWhile { it.isLetterOrDigit() || it == '$' || it == '_' }.joinToString("")
-    }
-    return null
-}
-private fun Cursor.tryParseJSNumber(): Number? {
-    if (peek().isDigit()) {
-        return nextWhile { it.isDigit() }.fold(0) { number, char -> number * 10 + char.digitToInt() }
-    }
-    return null
-}
-
-private fun Cursor.parseJSStringChar(start: Char): Char? {
-    val char = next()
-    if (char == '\\') {
-        return when (val esc = next()) {
-            '0' -> Char(0)
-            'b' -> '\b'
-            'f' -> TODO()
-            'n' -> '\n'
-            'r' -> '\r'
-            't' -> '\t'
-            'v' -> TODO()
-            '\'' -> '\''
-            '"' -> '"'
-            '\\' -> '\\'
-            'x' -> Char("${next()}${next()}".toInt(16))
-            'u' -> Char("${next()}${next()}${next()}${next()}".toInt(16))
-            else -> throw Exception("Unknown escaped char '$esc'")
-        }
-    }
-    if (char == start) return null
-    return char
-}
-
-private fun Cursor.tryParseJSString(): String? {
-    if (peek().let { it == '\'' || it == '"' }) {
-        val start = next()
-        var str = ""
-        while (true) {
-            val char = parseJSStringChar(start) ?: break
-            str += char
-        }
-        return str
-    }
-    return null
-}
-
-@OptIn(ExperimentalSerializationApi::class)
-private fun Cursor.parseJSPrimitive(): JsonPrimitive {
-    whitespace()
-    if (tryNext("null")) return JsonPrimitive(null)
-    tryParseJSBoolean()?.let { return JsonPrimitive(it) }
-    tryParseJSNumber()?.let { return JsonPrimitive(it) }
-    tryParseJSString()?.let { return JsonPrimitive(it) }
-
-    tryParseJSIdentifier()?.let { return JsonPrimitive("$$it") }
-    throw Exception("Unexpected character '${peek()}'")
-}
-
-private fun Cursor.parseJSElement(): JsonElement {
-    whitespace()
-    return when (peek()) {
-        '{' -> parseJSObject()
-        '[' -> parseJSArray()
-        else -> parseJSPrimitive()
-    }
-}
-
-object JSParser {
-    fun parseToJson(str: String): JsonElement {
-        Cursor(str, 0).run {
-            return parseJSElement().also {
-                whitespace()
-                if (hasNext()) throw Exception("Expected end of data")
-            }
-        }
-    }
-}
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/util/ReentrantMutex.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/util/ReentrantMutex.kt
deleted file mode 100644
index 2fac48e1e99702c7eabfff3095d4ce169217b094..0000000000000000000000000000000000000000
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/util/ReentrantMutex.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package net.novagamestudios.kaffeekasse.util
-
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
-import kotlinx.coroutines.withContext
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.coroutineContext
-
-
-//class ReentrantMutex {
-//    private val mutex = Mutex()
-//    val isLocked get() = mutex.isLocked
-//    suspend fun <T> withReentrantLock(block: suspend () -> T) = mutex.withReentrantLock(block)
-//}
-
-suspend fun <T> Mutex.withReentrantLock(block: suspend () -> T): T {
-    val key = ReentrantMutexContextKey(this)
-    if (coroutineContext[key] != null) return block()
-    return withContext(ReentrantMutexContextElement(key)) { withLock { block() } }
-}
-
-private class ReentrantMutexContextElement(override val key: ReentrantMutexContextKey) : CoroutineContext.Element
-private data class ReentrantMutexContextKey(val mutex: Mutex) : CoroutineContext.Key<ReentrantMutexContextElement>
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/util/Serialization.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/util/Serialization.kt
deleted file mode 100644
index d8a1431308dc7c77eb9ff5ec655b4e7cf224d24d..0000000000000000000000000000000000000000
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/util/Serialization.kt
+++ /dev/null
@@ -1,18 +0,0 @@
-package net.novagamestudios.kaffeekasse.util
-
-import kotlinx.serialization.KSerializer
-import kotlinx.serialization.descriptors.PrimitiveKind
-import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
-import kotlinx.serialization.encoding.Decoder
-import kotlinx.serialization.encoding.Encoder
-
-
-object IntAsBooleanSerializer : KSerializer<Boolean> {
-    override val descriptor = PrimitiveSerialDescriptor("IntAsBoolean", PrimitiveKind.INT)
-    override fun serialize(encoder: Encoder, value: Boolean) {
-        encoder.encodeInt(if (value) 1 else 0)
-    }
-    override fun deserialize(decoder: Decoder): Boolean {
-        return decoder.decodeInt() != 0
-    }
-}
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/util/Util.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/util/Util.kt
deleted file mode 100644
index 44d29309106ce4014dd9b0d0987b6761f677947e..0000000000000000000000000000000000000000
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/util/Util.kt
+++ /dev/null
@@ -1,37 +0,0 @@
-package net.novagamestudios.kaffeekasse.util
-
-import kotlinx.coroutines.flow.FlowCollector
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.datetime.LocalTime
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.nanoseconds
-
-
-operator fun LocalTime.minus(other: LocalTime): Duration {
-    return (toNanosecondOfDay() - other.toNanosecondOfDay()).nanoseconds
-}
-
-
-inline fun Mutex.tryWithLock(owner: Any? = null, block: () -> Unit): Boolean {
-    if (!tryLock(owner)) return false
-    try {
-        block()
-    } finally {
-        unlock(owner)
-    }
-    return true
-}
-
-fun <T, R> StateFlow<T>.mapState(
-    transform: (T) -> R
-): StateFlow<R> = object : StateFlow<R> {
-    override val replayCache: List<R> get() = this@mapState.replayCache.map(transform)
-    override val value: R get() = transform(this@mapState.value)
-    override suspend fun collect(collector: FlowCollector<R>): Nothing {
-        this@mapState.collect { value -> collector.emit(transform(value)) }
-    }
-}
-
-context (T)
-fun <T> context(): T = this@T
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/util/richdata/RichDataFlow.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/util/richdata/RichDataFlow.kt
index 71c1a3a27970700897ae367854a1c6cde4e1998f..e66a320aa3373b36a13c00de9a03d6f7499ff939 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/util/richdata/RichDataFlow.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/util/richdata/RichDataFlow.kt
@@ -13,8 +13,8 @@ import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.flow.transform
 import kotlinx.coroutines.flow.transformLatest
+import net.novagamestudios.common_utils.collection.mapState
 import net.novagamestudios.common_utils.compose.components.Progress
-import net.novagamestudios.kaffeekasse.util.mapState
 
 
 typealias RichDataFlow<T> = Flow<RichData<T>>
diff --git a/app/src/main/java/net/novagamestudios/kaffeekasse/util/richdata/RichDataSource.kt b/app/src/main/java/net/novagamestudios/kaffeekasse/util/richdata/RichDataSource.kt
index 403996b5c0b4eba0bdad85a278afe286cf1df69c..0edfe02b366c2904211afdb7fc906cdafb780bde 100644
--- a/app/src/main/java/net/novagamestudios/kaffeekasse/util/richdata/RichDataSource.kt
+++ b/app/src/main/java/net/novagamestudios/kaffeekasse/util/richdata/RichDataSource.kt
@@ -7,9 +7,8 @@ import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.launchIn
-import net.novagamestudios.common_utils.Logger
 import net.novagamestudios.common_utils.compose.components.Progress
-import net.novagamestudios.common_utils.error
+import net.novagamestudios.common_utils.logging.Logger
 import kotlin.coroutines.suspendCoroutine
 
 typealias RichDataCollector<T> = FlowCollector<RichData<T>>
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b04dcf62f344d57f9e68ee447da76c4cbf636ac9..0f97579c3f30575cba2272f394498852b0914db3 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -10,7 +10,7 @@ vico = "2.0.0-alpha.8"
 voyager = "1.0.0"
 coil = "2.6.0"
 acra = "5.11.3"
-commonutils = "ee87e097c5"
+commonutils = "e4ef869304"
 junit4 = "4.13.2"
 
 [plugins]
@@ -83,10 +83,13 @@ acra-toast = { group = "ch.acra", name = "acra-toast", version.ref = "acra" }
 acra-limiter = { group = "ch.acra", name = "acra-limiter", version.ref = "acra" }
 acra-advancedscheduler = { group = "ch.acra", name = "acra-advanced-scheduler", version.ref = "acra" }
 
+# JitPack
 commonutils-core = { group = "com.gitlab.JojoIV", name = "common_utils-core", version.ref = "commonutils" }
+commonutils-compose = { group = "com.gitlab.JojoIV", name = "common_utils-compose", version.ref = "commonutils" }
 commonutils-voyager = { group = "com.gitlab.JojoIV", name = "common_utils-voyager", version.ref = "commonutils" }
 # Maven Local
 #commonutils-core = { group = "net.novagamestudios.common_utils", name = "common_utils-core", version = "unspecified" }
+#commonutils-compose = { group = "net.novagamestudios.common_utils", name = "common_utils-compose", version = "unspecified" }
 #commonutils-voyager = { group = "net.novagamestudios.common_utils", name = "common_utils-voyager", version = "unspecified" }
 
 junit4 = { group = "junit", name = "junit", version.ref = "junit4" }