diff --git a/.idea/runConfigurations/Run.xml b/.idea/runConfigurations/Run.xml
index beddb34..28f97a7 100644
--- a/.idea/runConfigurations/Run.xml
+++ b/.idea/runConfigurations/Run.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/.idea/runConfigurations/Run_Standalone.xml b/.idea/runConfigurations/Run_Standalone.xml
index 71c7d3c..74fed74 100644
--- a/.idea/runConfigurations/Run_Standalone.xml
+++ b/.idea/runConfigurations/Run_Standalone.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/core/design-system/src/main/kotlin/app/cleanmeter/core/designsystem/Typography.kt b/core/design-system/src/main/kotlin/app/cleanmeter/core/designsystem/Typography.kt
index f26e28c..d20a8bd 100644
--- a/core/design-system/src/main/kotlin/app/cleanmeter/core/designsystem/Typography.kt
+++ b/core/design-system/src/main/kotlin/app/cleanmeter/core/designsystem/Typography.kt
@@ -9,16 +9,28 @@ import androidx.compose.ui.unit.sp
class Typography {
+ /**
+ * fontSize = 32.sp,
+ * lineHeight = 0.sp,
+ * fontWeight = W400,
+ */
+ val titleXXL: TextStyle
+ @Composable get() = defaultTextStyle.copy(
+ fontSize = 32.sp,
+ lineHeight = 0.sp,
+ fontFamily = fontFamilyNormal,
+ )
+
/**
* fontSize = 16.sp,
* lineHeight = 0.sp,
- * fontWeight = W500,
+ * fontWeight = W400,
*/
val titleM: TextStyle
@Composable get() = defaultTextStyle.copy(
fontSize = 16.sp,
lineHeight = 0.sp,
- fontFamily = fontFamilyMedium,
+ fontFamily = fontFamilyNormal,
)
/**
diff --git a/target/desktop/build.gradle.kts b/target/desktop/build.gradle.kts
index 073f0d5..cdfddcc 100644
--- a/target/desktop/build.gradle.kts
+++ b/target/desktop/build.gradle.kts
@@ -65,7 +65,7 @@ compose.desktop {
}
}
- mainClass = "app.cleanmeter.target.desktop.ServerMainKt"
+ mainClass = "app.cleanmeter.target.desktop.DesktopMainKt"
buildTypes.release.proguard {
version.set("7.5.0")
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/DesktopMain.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/DesktopMain.kt
new file mode 100644
index 0000000..966e887
--- /dev/null
+++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/DesktopMain.kt
@@ -0,0 +1,29 @@
+package app.cleanmeter.target.desktop
+
+import app.cleanmeter.core.common.process.singleInstance
+import app.cleanmeter.core.common.reporting.ApplicationParams
+import app.cleanmeter.core.os.ProcessManager
+import app.cleanmeter.core.os.util.isDev
+import app.cleanmeter.core.os.win32.WindowsService
+import app.cleanmeter.target.desktop.data.PREFERENCE_PERMISSION_CONSENT
+import app.cleanmeter.target.desktop.data.PreferencesRepository
+
+fun main(vararg args: String) = singleInstance(args) {
+ if (PreferencesRepository.getPreferenceBoolean(PREFERENCE_PERMISSION_CONSENT, false)) {
+ WindowsService.tryElevateProcess(ApplicationParams.isAutostart)
+
+ if (isDev()) {
+ Runtime.getRuntime().addShutdownHook(Thread {
+ ProcessManager.stop()
+ })
+ } else {
+ KeyboardManager.registerKeyboardHook()
+ }
+
+ if (!ApplicationParams.isAutostart) {
+ ProcessManager.start()
+ }
+ }
+
+ composeApp()
+}
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ServerMain.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ServerMain.kt
deleted file mode 100644
index 2380088..0000000
--- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ServerMain.kt
+++ /dev/null
@@ -1,28 +0,0 @@
-package app.cleanmeter.target.desktop
-
-import app.cleanmeter.core.common.process.singleInstance
-import app.cleanmeter.core.common.reporting.ApplicationParams
-import app.cleanmeter.core.os.ProcessManager
-import app.cleanmeter.core.os.util.isDev
-import app.cleanmeter.core.os.win32.WindowsService
-
-
-fun main(vararg args: String) = singleInstance(args) {
- System.setProperty("awt.useSystemAAFontSettings","on");
- System.setProperty("swing.aatext", "true");
- WindowsService.tryElevateProcess(ApplicationParams.isAutostart)
-
- if (isDev()) {
- Runtime.getRuntime().addShutdownHook(Thread {
- ProcessManager.stop()
- })
- } else {
- KeyboardManager.registerKeyboardHook()
- }
-
- if (!ApplicationParams.isAutostart) {
- ProcessManager.start()
- }
-
- composeApp()
-}
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferencesRepository.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferencesRepository.kt
index ff7f9e4..b9573f3 100644
--- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferencesRepository.kt
+++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/data/PreferencesRepository.kt
@@ -7,6 +7,7 @@ import java.util.prefs.Preferences
const val OVERLAY_SETTINGS_PREFERENCE_KEY = "OVERLAY_SETTINGS_PREFERENCE_KEY"
const val PREFERENCE_START_MINIMIZED = "PREFERENCE_START_MINIMIZED"
+const val PREFERENCE_PERMISSION_CONSENT = "PREFERENCE_PERMISSION_CONSENT"
object PreferencesRepository {
@@ -24,6 +25,7 @@ object PreferencesRepository {
fun setPreference(key: String, value: String) = prefs.put(key, value)
fun setPreferenceBoolean(key: String, value: Boolean) = prefs.putBoolean(key, value)
+ fun clear() = prefs.clear()
}
fun PreferencesRepository.loadOverlaySettings(): OverlaySettings {
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/Button.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/Button.kt
new file mode 100644
index 0000000..6623ef5
--- /dev/null
+++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/Button.kt
@@ -0,0 +1,61 @@
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Text
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+import app.cleanmeter.core.designsystem.LocalColorScheme
+import app.cleanmeter.core.designsystem.LocalTypography
+
+@Composable
+internal fun ClearButton(
+ label: String,
+ textColor: Color = LocalColorScheme.current.text.inverse,
+ onClick: () -> Unit
+) {
+ Button(
+ onClick = onClick,
+ colors = ButtonDefaults.textButtonColors(
+ containerColor = Color.Transparent,
+ ),
+ contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
+ ) {
+ Text(
+ text = label,
+ style = LocalTypography.current.labelLMedium,
+ color = textColor,
+ modifier = Modifier.wrapContentHeight(),
+ )
+ }
+}
+
+@Composable
+internal fun FilledButton(
+ label: String,
+ containerColor: Color = LocalColorScheme.current.background.surfaceRaised,
+ textColor: Color = LocalColorScheme.current.text.heading,
+ textStyle: TextStyle = LocalTypography.current.labelLMedium,
+ contentPadding: PaddingValues = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
+ onClick: () -> Unit
+) {
+ Button(
+ onClick = onClick,
+ colors = ButtonDefaults.textButtonColors(
+ containerColor = containerColor,
+ ),
+ contentPadding = contentPadding,
+ shape = RoundedCornerShape(100),
+ ) {
+ Text(
+ text = label,
+ color = textColor,
+ style = textStyle,
+ modifier = Modifier.wrapContentHeight(),
+ )
+ }
+}
\ No newline at end of file
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/UpdateToast.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/UpdateToast.kt
index 1e92ece..4e254a0 100644
--- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/UpdateToast.kt
+++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/components/UpdateToast.kt
@@ -1,5 +1,7 @@
package app.cleanmeter.target.desktop.ui.components
+import ClearButton
+import FilledButton
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.slideInVertically
@@ -128,42 +130,7 @@ private fun RowScope.BodyText(state: UpdateState) {
}
}
-@Composable
-private fun ClearButton(onClick: () -> Unit, label: String) {
- Button(
- onClick = onClick,
- colors = ButtonDefaults.textButtonColors(
- containerColor = Color.Transparent,
- ),
- contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
- ) {
- Text(
- text = label,
- style = LocalTypography.current.labelLMedium,
- color = LocalColorScheme.current.text.inverse,
- modifier = Modifier.wrapContentHeight(),
- )
- }
-}
-@Composable
-private fun FilledButton(onClick: () -> Unit, label: String) {
- Button(
- onClick = onClick,
- colors = ButtonDefaults.textButtonColors(
- containerColor = LocalColorScheme.current.background.surfaceRaised,
- ),
- contentPadding = PaddingValues(horizontal = 8.dp, vertical = 0.dp),
- shape = RoundedCornerShape(100),
- ) {
- Text(
- text = label,
- color = LocalColorScheme.current.text.heading,
- style = LocalTypography.current.labelLMedium,
- modifier = Modifier.wrapContentHeight(),
- )
- }
-}
@Composable
private fun RowScope.CallToAction(
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/Settings.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/Settings.kt
index 2f8d341..2da003f 100644
--- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/Settings.kt
+++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/Settings.kt
@@ -1,33 +1,45 @@
package app.cleanmeter.target.desktop.ui.settings
+import FilledButton
+import androidx.compose.foundation.Image
import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.window.WindowDraggableArea
import androidx.compose.material.ScrollableTabRow
+import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.WindowScope
import androidx.lifecycle.viewmodel.compose.viewModel
import app.cleanmeter.core.common.hardwaremonitor.cpuReadings
import app.cleanmeter.core.common.hardwaremonitor.gpuReadings
import app.cleanmeter.core.common.hardwaremonitor.networkReadings
import app.cleanmeter.core.designsystem.LocalColorScheme
+import app.cleanmeter.core.designsystem.LocalTypography
import app.cleanmeter.target.desktop.ui.AppTheme
import app.cleanmeter.target.desktop.ui.components.SettingsTab
import app.cleanmeter.target.desktop.ui.components.TopBar
@@ -45,7 +57,8 @@ fun WindowScope.Settings(
viewModel: SettingsViewModel = viewModel(),
onCloseRequest: () -> Unit,
onMinimizeRequest: () -> Unit,
- getOverlayPosition: () -> IntOffset
+ getOverlayPosition: () -> IntOffset,
+ onExitRequest: () -> Unit,
) = AppTheme(isDarkTheme) {
val settingsState by viewModel.state.collectAsState(SettingsState())
val updaterState by AutoUpdater.state.collectAsState()
@@ -66,22 +79,94 @@ fun WindowScope.Settings(
var selectedTabIndex by remember { mutableStateOf(0) }
Box(modifier = Modifier.fillMaxSize()) {
- Column(modifier = Modifier.fillMaxSize().padding(24.dp)) {
- TabRow(selectedTabIndex) {
- selectedTabIndex = it
+ if (!settingsState.adminConsent) {
+ AdminConsent(
+ isDarkTheme = isDarkTheme,
+ onDeny = onExitRequest,
+ onAllow = {
+ viewModel.onEvent(SettingsEvent.ConsentGiven)
+ }
+ )
+ } else {
+ Column(modifier = Modifier.fillMaxSize().padding(24.dp)) {
+ TabRow(selectedTabIndex) {
+ selectedTabIndex = it
+ }
+
+ TabContent(
+ selectedTabIndex = selectedTabIndex,
+ settingsState = settingsState,
+ viewModel = viewModel,
+ getOverlayPosition = getOverlayPosition,
+ )
}
- TabContent(
- selectedTabIndex = selectedTabIndex,
- settingsState = settingsState,
- viewModel = viewModel,
- getOverlayPosition = getOverlayPosition,
- )
+ if (updaterState !is UpdateState.NotAvailable) {
+ UpdateToast()
+ }
}
+ }
+ }
+}
- if (updaterState !is UpdateState.NotAvailable) {
- UpdateToast()
- }
+@Composable
+private fun BoxScope.AdminConsent(
+ isDarkTheme: Boolean,
+ onDeny: () -> Unit,
+ onAllow: () -> Unit
+) {
+ Column(
+ modifier = Modifier.fillMaxSize().padding(48.dp).align(Alignment.Center),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(24.dp, Alignment.CenterVertically),
+ ) {
+ Box(
+ modifier = Modifier
+ .width(400.dp)
+ .background(LocalColorScheme.current.background.surfaceRaised, RoundedCornerShape(12.dp))
+ .padding(top = 24.dp)
+ ,
+ contentAlignment = Alignment.Center
+ ) {
+ Image(
+ painter = painterResource("icons/onboarding_${if (isDarkTheme) "dark" else "light"}.png"),
+ contentDescription = null
+ )
+ }
+
+ Text(
+ text = "Administrative Privileges",
+ style = LocalTypography.current.titleXXL,
+ color = LocalColorScheme.current.text.heading,
+ )
+
+ Text(
+ text = "Thank you for choosing CleanMeter!\n\n" +
+ "To function properly, CleanMeter requires administrative permissions and access to your local network. This is necessary for our processes to communicate with each other using sockets.\n\n" +
+ "If you’re okay with this, please grant the permissions below.",
+ textAlign = TextAlign.Center,
+ style = LocalTypography.current.labelLMedium.copy(
+ lineHeight = 20.sp,
+ ),
+ color = LocalColorScheme.current.text.paragraph1,
+ )
+
+ Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
+ FilledButton(
+ label = "Close app",
+ containerColor = LocalColorScheme.current.background.surfaceRaised,
+ textStyle = LocalTypography.current.titleMMedium,
+ contentPadding = PaddingValues(horizontal = 32.dp, vertical = 16.dp),
+ onClick = onDeny
+ )
+ FilledButton(
+ label = "Allow",
+ containerColor = LocalColorScheme.current.background.brand,
+ textColor = LocalColorScheme.current.text.inverse,
+ textStyle = LocalTypography.current.titleMMedium,
+ contentPadding = PaddingValues(horizontal = 32.dp, vertical = 16.dp),
+ onClick = onAllow
+ )
}
}
}
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsViewModel.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsViewModel.kt
index 192baf6..98657b4 100644
--- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsViewModel.kt
+++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsViewModel.kt
@@ -3,33 +3,33 @@ package app.cleanmeter.target.desktop.ui.settings
import androidx.compose.ui.unit.IntOffset
import androidx.lifecycle.ViewModel
import app.cleanmeter.core.common.hardwaremonitor.HardwareMonitorData
+import app.cleanmeter.core.common.reporting.ApplicationParams
import app.cleanmeter.core.os.hardwaremonitor.HardwareMonitorReader
import app.cleanmeter.core.os.hardwaremonitor.Packet
import app.cleanmeter.core.os.hardwaremonitor.SocketClient
+import app.cleanmeter.core.os.win32.WindowsService
import app.cleanmeter.target.desktop.KeyboardEvent
import app.cleanmeter.target.desktop.KeyboardManager
import app.cleanmeter.target.desktop.data.OverlaySettingsRepository
+import app.cleanmeter.target.desktop.data.PREFERENCE_PERMISSION_CONSENT
+import app.cleanmeter.target.desktop.data.PreferencesRepository
import app.cleanmeter.target.desktop.model.OverlaySettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.flow.distinctUntilChangedBy
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import java.io.File
-import java.io.Writer
data class SettingsState(
val overlaySettings: OverlaySettings? = null,
val hardwareData: HardwareMonitorData? = null,
val isRecording: Boolean = false,
+ val adminConsent: Boolean = false,
)
sealed class SettingsEvent {
@@ -46,6 +46,7 @@ sealed class SettingsEvent {
data class DarkThemeToggle(val isEnabled: Boolean) : SettingsEvent()
data class FpsApplicationSelect(val applicationName: String) : SettingsEvent()
data class BoundarySet(val sensorType: SensorType, val boundaries: OverlaySettings.Sensor.GraphSensor.Boundaries) : SettingsEvent()
+ data object ConsentGiven : SettingsEvent()
}
class SettingsViewModel : ViewModel() {
@@ -61,6 +62,12 @@ class SettingsViewModel : ViewModel() {
observeData()
observeRecordingHotkey()
observeRecordingState()
+
+ _state.update {
+ it.copy(
+ adminConsent = PreferencesRepository.getPreferenceBoolean(PREFERENCE_PERMISSION_CONSENT, false)
+ )
+ }
}
private fun observeOverlaySettings() {
@@ -103,12 +110,14 @@ class SettingsViewModel : ViewModel() {
dataHistory.add(state.hardwareData)
return@collectLatest
}
+
!state.isRecording && dataHistory.isNotEmpty() -> {
File("cleanmeter.recording.${System.currentTimeMillis()}.json").printWriter().use {
it.append(Json.encodeToString(dataHistory))
dataHistory.clear()
}
}
+
dataHistory.isNotEmpty() -> dataHistory.clear()
else -> Unit
}
@@ -131,9 +140,16 @@ class SettingsViewModel : ViewModel() {
is SettingsEvent.DarkThemeToggle -> onDarkModeToggle(event.isEnabled, this)
is SettingsEvent.FpsApplicationSelect -> onFpsApplicationSelect(event.applicationName, this)
is SettingsEvent.BoundarySet -> onBoundarySet(event.sensorType, event.boundaries, this)
+ is SettingsEvent.ConsentGiven -> onConsentGiven()
}
}
+ private fun onConsentGiven() {
+ PreferencesRepository.setPreferenceBoolean(PREFERENCE_PERMISSION_CONSENT, true)
+ _state.update { it.copy(adminConsent = true) }
+ WindowsService.tryElevateProcess(ApplicationParams.isAutostart)
+ }
+
private fun onBoundarySet(
sensorType: SensorType,
boundaries: OverlaySettings.Sensor.GraphSensor.Boundaries,
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsWindow.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsWindow.kt
index fe60f70..4380197 100644
--- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsWindow.kt
+++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/SettingsWindow.kt
@@ -62,7 +62,7 @@ fun ApplicationScope.SettingsWindow(
if (it.width != 650.dp) {
size = it.copy(width = 650.dp)
}
- if (it.height < minimumHeight.dp || it.height > maximumWindowBounds.dp ) {
+ if (it.height < minimumHeight.dp || it.height > maximumWindowBounds.dp) {
size = it.copy(height = minimumHeight.dp)
}
state.size = size
@@ -74,7 +74,8 @@ fun ApplicationScope.SettingsWindow(
isDarkTheme = isDarkTheme,
onCloseRequest = { isVisible = false },
onMinimizeRequest = { state.isMinimized = true },
- getOverlayPosition = getOverlayPosition
+ getOverlayPosition = getOverlayPosition,
+ onExitRequest = { exitApplication() }
)
}
diff --git a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/tabs/AppSettingsUi.kt b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/tabs/AppSettingsUi.kt
index eac0e08..99b08fc 100644
--- a/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/tabs/AppSettingsUi.kt
+++ b/target/desktop/src/main/kotlin/app/cleanmeter/target/desktop/ui/settings/tabs/AppSettingsUi.kt
@@ -1,5 +1,6 @@
package app.cleanmeter.target.desktop.ui.settings.tabs
+import ClearButton
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.TooltipArea
@@ -48,6 +49,9 @@ fun AppSettingsUi(
) {
startWithWindowsCheckbox()
startMinimizedCheckbox()
+ ClearButton(label = "Clear app preferences", textColor = LocalColorScheme.current.text.heading) {
+ PreferencesRepository.clear()
+ }
}
}
diff --git a/target/desktop/src/main/resources/icons/onboarding_dark.png b/target/desktop/src/main/resources/icons/onboarding_dark.png
new file mode 100644
index 0000000..8ec85ef
Binary files /dev/null and b/target/desktop/src/main/resources/icons/onboarding_dark.png differ
diff --git a/target/desktop/src/main/resources/icons/onboarding_light.png b/target/desktop/src/main/resources/icons/onboarding_light.png
new file mode 100644
index 0000000..069f628
Binary files /dev/null and b/target/desktop/src/main/resources/icons/onboarding_light.png differ