Merge branch 'main' into feat-performances-screen

This commit is contained in:
Florent CHAMPIGNY 2026-01-25 22:30:53 +01:00 committed by GitHub
commit efcc466404
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 180 additions and 137 deletions

View file

@ -49,6 +49,7 @@ internal class FloconHttpClientAndroid : FloconHttpClient {
val request = Request.Builder()
.url(uploadUrl)
.header("User-Agent", "Flocon")
.post(multipartBody)
.build()

View file

@ -72,6 +72,7 @@ internal class FloconWebSocketClientImpl : FloconWebSocketClient {
val request = Request.Builder()
.url("ws://$address:$port")
.header("User-Agent", "Flocon")
.build()
val newWebSocket = client.newWebSocket(request, connectionListener)

View file

@ -5,8 +5,11 @@ import io.github.openflocon.flocon.model.FloconFileInfo
import io.ktor.client.HttpClient
import io.ktor.client.engine.darwin.Darwin
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.logging.LogLevel
import io.ktor.client.plugins.logging.Logging
import io.ktor.client.request.header
import io.ktor.http.HttpHeaders
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
@ -27,6 +30,9 @@ internal class FloconHttpClientIOs() : FloconHttpClient {
install(Logging) {
level = LogLevel.INFO
}
defaultRequest {
header(HttpHeaders.UserAgent, "Flocon")
}
}
override suspend fun send(

View file

@ -2,12 +2,23 @@ package io.github.openflocon.flocon.websocket
import io.github.openflocon.flocon.FloconLogger
import io.ktor.client.*
import io.ktor.client.HttpClient
import io.ktor.client.engine.darwin.Darwin
import io.ktor.client.plugins.websocket.*
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.client.plugins.websocket.webSocketSession
import io.ktor.client.request.header
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.websocket.*
import kotlinx.coroutines.*
import io.ktor.websocket.CloseReason
import io.ktor.websocket.Frame
import io.ktor.websocket.close
import io.ktor.websocket.readReason
import io.ktor.websocket.readText
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
internal actual fun buildFloconWebSocketClient(): FloconWebSocketClient {
return FloconWebSocketClientIOs()
@ -17,6 +28,9 @@ internal class FloconWebSocketClientIOs() : FloconWebSocketClient {
private val client = HttpClient(Darwin.create()) {
install(WebSockets)
defaultRequest {
header(HttpHeaders.UserAgent, "Flocon")
}
}
private var session: DefaultClientWebSocketSession? = null

View file

@ -12,6 +12,7 @@ import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.http.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.forms.*
import io.ktor.client.plugins.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
@ -32,6 +33,9 @@ internal class FloconHttpClientJvm() : FloconHttpClient {
install(Logging) {
level = LogLevel.INFO
}
defaultRequest {
header(HttpHeaders.UserAgent, "Flocon")
}
}
override suspend fun send(

View file

@ -1,13 +1,23 @@
package io.github.openflocon.flocon.websocket
import io.github.openflocon.flocon.FloconLogger
import io.ktor.client.*
import io.ktor.client.engine.cio.CIO
import io.ktor.client.plugins.websocket.*
import io.ktor.client.HttpClient
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession
import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.client.plugins.websocket.webSocketSession
import io.ktor.client.request.header
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.websocket.*
import kotlinx.coroutines.*
import io.ktor.websocket.CloseReason
import io.ktor.websocket.Frame
import io.ktor.websocket.close
import io.ktor.websocket.readReason
import io.ktor.websocket.readText
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
internal actual fun buildFloconWebSocketClient(): FloconWebSocketClient {
return FloconWebSocketClientJvm()
@ -17,6 +27,9 @@ internal class FloconWebSocketClientJvm() : FloconWebSocketClient {
private val client = HttpClient(CIO.create()) {
install(WebSockets)
defaultRequest {
header(HttpHeaders.UserAgent, "Flocon")
}
}
private var session: DefaultClientWebSocketSession? = null

View file

@ -69,6 +69,8 @@
<string name="settings_adb_valid">ADB configuraton is valid</string>
<string name="settings_font_size_multiplier">Font Size Multiplier : %1$sx</string>
<string name="settings_test">Test</string>
<string name="settings_about_title">About</string>
<string name="settings_licenses">Licenses</string>
<string name="size">Size</string>
<string name="tables_empty">No Tables</string>
<string name="time">Time</string>

View file

@ -25,10 +25,14 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.touchlab.kermit.Logger
import flocondesktop.composeapp.generated.resources.Res
import flocondesktop.composeapp.generated.resources.general_save
import flocondesktop.composeapp.generated.resources.settings_about_title
import flocondesktop.composeapp.generated.resources.settings_adb_setup_title
import flocondesktop.composeapp.generated.resources.settings_adb_valid
import flocondesktop.composeapp.generated.resources.settings_font_size_multiplier
import flocondesktop.composeapp.generated.resources.settings_licenses
import flocondesktop.composeapp.generated.resources.settings_test
import io.github.openflocon.flocondesktop.common.ui.window.FloconWindow
import io.github.openflocon.flocondesktop.common.ui.window.createFloconWindowState
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.FloconButton
import io.github.openflocon.library.designsystem.components.FloconFeature
@ -73,6 +77,8 @@ private fun SettingsScreen(
onAction: (SettingsAction) -> Unit,
modifier: Modifier = Modifier,
) {
var showLicenses by remember { mutableStateOf(false) }
FloconFeature(
modifier = modifier.fillMaxSize()
) {
@ -151,6 +157,22 @@ private fun SettingsScreen(
)
}
}
FloconSection(
title = stringResource(Res.string.settings_about_title),
initialValue = true
) {
SettingsButton(
onClick = { showLicenses = true },
text = stringResource(Res.string.settings_licenses),
modifier = Modifier.padding(8.dp)
)
}
}
if (showLicenses) {
LicensesWindow(
onCloseRequest = { showLicenses = false }
)
}
}
@ -172,6 +194,25 @@ private fun SettingsButton(
}
}
@Composable
private fun LicensesWindow(
onCloseRequest: () -> Unit
) {
FloconWindow(
title = "Licenses",
state = createFloconWindowState(),
alwaysOnTop = true,
onCloseRequest = onCloseRequest,
) {
AboutScreen(
modifier = Modifier
.fillMaxSize()
.background(FloconTheme.colorPalette.primary),
)
}
}
@Preview
@Composable
private fun SettingsScreenPreview() {

View file

@ -3,6 +3,7 @@
package io.github.openflocon.flocondesktop.app.ui.view.topbar.app
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.Text
@ -67,7 +68,7 @@ internal fun TopBarAppDropdown(
FloconExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier.exposedDropdownSize()
modifier = Modifier.exposedDropdownSize(matchAnchorWidth = false)
) {
appsState.apps
.fastForEach { app ->
@ -79,10 +80,12 @@ internal fun TopBarAppDropdown(
deleteApp(app)
expanded = false
},
modifier = Modifier.clickable {
onAppSelected(app)
expanded = false
}
modifier = Modifier
.fillMaxWidth()
.clickable {
onAppSelected(app)
expanded = false
}
)
}
}

View file

@ -46,7 +46,7 @@ internal fun TopBarAppView(
deleteClick: (() -> Unit)? = null,
) {
Row(
modifier = modifier.padding(horizontal = 8.dp, 4.dp),
modifier = modifier.padding(horizontal = 8.dp, vertical = 4.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {

View file

@ -2,10 +2,10 @@
package io.github.openflocon.flocondesktop.app.ui.view.topbar.device
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -22,6 +22,8 @@ import io.github.openflocon.flocondesktop.app.ui.model.DevicesStateUiModel
import io.github.openflocon.flocondesktop.app.ui.view.topbar.TopBarSelector
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.FloconCircularProgressIndicator
import io.github.openflocon.library.designsystem.components.FloconExposedDropdownMenu
import io.github.openflocon.library.designsystem.components.FloconExposedDropdownMenuBox
import org.jetbrains.compose.resources.stringResource
@Composable
@ -33,7 +35,7 @@ internal fun TopBarDeviceDropdown(
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
FloconExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = {
expanded = it
@ -55,24 +57,23 @@ internal fun TopBarDeviceDropdown(
)
}
}
ExposedDropdownMenu(
FloconExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
containerColor = FloconTheme.colorPalette.primary,
shadowElevation = 0.dp,
shape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp),
modifier = Modifier.exposedDropdownSize(),
modifier = Modifier.exposedDropdownSize(matchAnchorWidth = false),
) {
if (state is DevicesStateUiModel.WithDevices) {
state.devices.forEach { device ->
TopBarDeviceView(
modifier = Modifier
.fillMaxWidth()
.clickable {
onDeviceSelected(device)
expanded = false
},
device = device,
selected = state.deviceSelected.id == device.id,
onClick = {
onDeviceSelected(device)
expanded = false
},
onDelete = {
deleteClick = {
deleteDevice(device)
expanded = false
},

View file

@ -25,11 +25,13 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import flocondesktop.composeapp.generated.resources.Res
@ -45,25 +47,16 @@ import org.jetbrains.compose.resources.stringResource
internal fun TopBarDeviceView(
device: DeviceItemUiModel,
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
selected: Boolean = false,
onDelete: (() -> Unit)? = null,
deleteClick: (() -> Unit)? = null,
) {
Row(
modifier = modifier
.then(
if (onClick != null)
Modifier.clickable(onClick = onClick)
else
Modifier
)
.padding(horizontal = 8.dp, 4.dp),
modifier = modifier.padding(horizontal = 8.dp, vertical = 4.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Box(
modifier = Modifier
.padding(horizontal = 4.dp)
.graphicsLayer {
alpha = if (device.isActive) 1f else 0.4f
}
@ -100,48 +93,47 @@ internal fun TopBarDeviceView(
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Column(
modifier = Modifier,
verticalArrangement = Arrangement.Center
) {
Text(
text = device.deviceName,
color = FloconTheme.colorPalette.onPrimary,
style = FloconTheme.typography.bodySmall.copy(fontWeight = FontWeight.Bold),
)
Text(
text = if (device.isActive.not()) {
stringResource(Res.string.devices_disconnected)
} else {
stringResource(Res.string.devices_connected)
},
color = FloconTheme.colorPalette.onPrimary,
style = FloconTheme.typography.bodySmall.copy(
fontSize = 10.sp,
),
)
}
if (!selected && onDelete != null) {
Spacer(modifier = Modifier.weight(1f))
Box(
Modifier.clip(RoundedCornerShape(4.dp))
.background(
Color.White.copy(alpha = 0.8f)
).padding(2.dp).clickable {
onDelete()
},
contentAlignment = Alignment.Center,
) {
FloconIcon(
imageVector = Icons.Outlined.Close,
tint = FloconTheme.colorPalette.primary,
modifier = Modifier.size(14.dp)
)
Column(
modifier = Modifier
.graphicsLayer {
alpha = if (device.isActive) 1f else 0.4f
}
) {
Text(
text = device.deviceName,
style = FloconTheme.typography.bodySmall.copy(fontWeight = FontWeight.Bold),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = FloconTheme.colorPalette.onPrimary,
)
Text(
text = if (device.isActive.not()) {
stringResource(Res.string.devices_disconnected)
} else {
stringResource(Res.string.devices_connected)
},
color = FloconTheme.colorPalette.onPrimary,
style = FloconTheme.typography.bodySmall.copy(
fontSize = 10.sp,
),
)
}
if (!selected && deleteClick != null) {
Spacer(modifier = Modifier.weight(1f))
Box(
Modifier
.clip(RoundedCornerShape(4.dp))
.background(Color.White.copy(alpha = 0.8f))
.padding(2.dp)
.clickable(onClick = deleteClick),
contentAlignment = Alignment.Center,
) {
FloconIcon(
imageVector = Icons.Outlined.Close,
tint = FloconTheme.colorPalette.primary,
modifier = Modifier.size(14.dp)
)
}
}
}
@ -162,8 +154,7 @@ private fun TopBarDeviceViewPreview_desktop_enabled() {
canRestart = true,
),
selected = false,
onDelete = {},
onClick = {}
deleteClick = {},
)
}
}
@ -183,8 +174,7 @@ private fun TopBarDeviceViewPreview_desktop_disabled() {
canRestart = true,
),
selected = false,
onDelete = {},
onClick = {}
deleteClick = {},
)
}
}
@ -205,8 +195,7 @@ private fun TopBarDeviceViewPreview_iphone() {
canRestart = true,
),
selected = false,
onDelete = {},
onClick = {}
deleteClick = {},
)
}
}
@ -228,8 +217,7 @@ private fun TopBarDeviceViewPreview_iphone_disabled() {
canRestart = true,
),
selected = false,
onDelete = {},
onClick = {}
deleteClick = {},
)
}
}

View file

@ -19,5 +19,6 @@ fun toHttpMethodUi(httpMethod: String): NetworkMethodUi = when (httpMethod.lower
"put" -> NetworkMethodUi.Http.PUT
"post" -> NetworkMethodUi.Http.POST
"delete" -> NetworkMethodUi.Http.DELETE
"patch" -> NetworkMethodUi.Http.PATCH
else -> NetworkMethodUi.OTHER(httpMethod, icon = null)
}

View file

@ -31,6 +31,11 @@ sealed interface NetworkMethodUi {
override val text: String = "DELETE"
override val icon = null
}
data object PATCH : Http {
override val text: String = "PATCH"
override val icon = null
}
}
sealed interface GraphQl : NetworkMethodUi {
@ -70,6 +75,7 @@ sealed interface NetworkMethodUi {
Http.POST,
Http.PUT,
Http.DELETE,
Http.PATCH,
GraphQl.QUERY,
GraphQl.MUTATION,
Grpc,

View file

@ -42,6 +42,9 @@ val deleteMethodText = Color(0xFFDC3545)
val otherMethodBackground = Color(0xFF6C757D).copy(alpha = 0.3f) // Muted gray for OTHER
val otherMethodText = Color(0xFF6C757D)
val patchMethodBackground = Color(0xFF6f42c1).copy(alpha = 0.3f) // Muted purple for PATCH
val patchMethodText = Color(0xFF6f42c1)
val websocketMethodBackground = Color(0xFF17A2B8).copy(alpha = 0.3f) // Muted cyan for WEBSOCKET
val websocketMethodText = Color(0xFF17A2B8)
@ -66,6 +69,7 @@ fun MethodView(
is NetworkMethodUi.OTHER -> otherMethodBackground to otherMethodText
is NetworkMethodUi.Http.POST -> postMethodBackground to postMethodText
is NetworkMethodUi.Http.PUT -> putMethodBackground to putMethodText
is NetworkMethodUi.Http.PATCH -> patchMethodBackground to patchMethodText
is NetworkMethodUi.GraphQl.QUERY -> graphQlQueryMethodBackground to graphQlQueryMethodText
is NetworkMethodUi.GraphQl.MUTATION -> graphQlQueryMethodBackground to graphQlQueryMethodText
is NetworkMethodUi.Grpc -> grpcMethodBackground to grpcMethodText

View file

@ -12,8 +12,8 @@ import io.github.openflocon.flocondesktop.features.network.list.view.components.
import io.github.openflocon.flocondesktop.features.network.list.view.components.getMethodText
import io.github.openflocon.flocondesktop.features.network.list.view.components.grpcMethodBackground
import io.github.openflocon.flocondesktop.features.network.list.view.components.grpcMethodText
import io.github.openflocon.flocondesktop.features.network.list.view.components.otherMethodBackground
import io.github.openflocon.flocondesktop.features.network.list.view.components.otherMethodText
import io.github.openflocon.flocondesktop.features.network.list.view.components.patchMethodBackground
import io.github.openflocon.flocondesktop.features.network.list.view.components.patchMethodText
import io.github.openflocon.flocondesktop.features.network.list.view.components.postMethodBackground
import io.github.openflocon.flocondesktop.features.network.list.view.components.postMethodText
import io.github.openflocon.flocondesktop.features.network.list.view.components.putMethodBackground
@ -30,7 +30,7 @@ fun MockNetworkMethodView(
when (method) {
MockNetworkMethodUi.DELETE -> deleteMethodBackground to deleteMethodText
MockNetworkMethodUi.GET -> getMethodBackground to getMethodText
MockNetworkMethodUi.PATCH -> otherMethodBackground to otherMethodText
MockNetworkMethodUi.PATCH -> patchMethodBackground to patchMethodText
MockNetworkMethodUi.POST -> postMethodBackground to postMethodText
MockNetworkMethodUi.PUT -> putMethodBackground to putMethodText
MockNetworkMethodUi.ALL -> grpcMethodBackground to grpcMethodText

View file

@ -1,7 +1,5 @@
package io.github.openflocon.flocondesktop
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
@ -10,14 +8,11 @@ import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type
import androidx.compose.ui.window.ApplicationScope
import androidx.compose.ui.window.FrameWindowScope
import androidx.compose.ui.window.MenuBar
import androidx.compose.ui.window.Notification
import androidx.compose.ui.window.Tray
import androidx.compose.ui.window.Window
@ -32,21 +27,17 @@ import flocondesktop.composeapp.generated.resources.app_icon_small
import io.github.openflocon.domain.feedback.FeedbackDisplayer
import io.github.openflocon.domain.feedback.FeedbackDisplayerHandler
import io.github.openflocon.flocondesktop.about.AboutScreen
import io.github.openflocon.flocondesktop.common.ui.window.FloconWindow
import io.github.openflocon.flocondesktop.common.ui.window.createFloconWindowState
import io.github.openflocon.flocondesktop.window.MIN_WINDOW_HEIGHT
import io.github.openflocon.flocondesktop.window.MIN_WINDOW_WIDTH
import io.github.openflocon.flocondesktop.window.WindowStateData
import io.github.openflocon.flocondesktop.window.WindowStateSaver
import io.github.openflocon.flocondesktop.window.size
import io.github.openflocon.flocondesktop.window.windowPosition
import io.github.openflocon.library.designsystem.FloconTheme
import io.github.openflocon.library.designsystem.components.escape.LocalEscapeHandlerStack
import org.jetbrains.compose.resources.painterResource
import org.koin.compose.koinInject
import java.awt.Desktop
import java.awt.Dimension
import java.util.Locale
private const val ACTIVATE_TRAY_NOTIFICATION = false
@ -117,7 +108,6 @@ fun main() {
if (ACTIVATE_TRAY_NOTIFICATION) {
FloconTray()
}
FloconMenu()
if (openAbout) {
AboutScreen(
@ -129,39 +119,6 @@ fun main() {
}
}
@Composable
private fun FrameWindowScope.FloconMenu() {
var openLicenses by remember { mutableStateOf(false) }
MenuBar {
Menu(
text = "Settings"
) {
Item(
text = "Licences",
onClick = {
openLicenses = true
}
)
}
}
if (openLicenses) {
FloconWindow(
title = "Licenses",
state = createFloconWindowState(),
alwaysOnTop = true,
onCloseRequest = { openLicenses = false },
) {
io.github.openflocon.flocondesktop.app.ui.settings.AboutScreen(
modifier = Modifier
.fillMaxSize()
.background(FloconTheme.colorPalette.primary),
)
}
}
}
@Composable
private fun ApplicationScope.FloconTray() {
val trayState = rememberTrayState()

View file

@ -37,6 +37,7 @@ fun ExposedDropdownMenuBoxScope.FloconExposedDropdownMenu(
onDismissRequest = onDismissRequest,
modifier = modifier,
containerColor = FloconTheme.colorPalette.primary,
matchAnchorWidth = false,
content = content
)
}

View file

@ -26,6 +26,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.ColorFilter.Companion.tint
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
@ -196,6 +197,7 @@ fun WithTooltip(
Text(
text = tooltip,
style = FloconTheme.typography.labelSmall,
color = FloconTheme.colorPalette.onPrimary,
modifier = Modifier
.clip(FloconTheme.shapes.small)
.background(FloconTheme.colorPalette.primary)
@ -239,7 +241,6 @@ fun FloconIconButton(
modifier: Modifier = Modifier,
tooltip: String? = null,
enabled: Boolean = true,
tint: Color = LocalContentColor.current
) {
WithTooltip(tooltip) {
FloconIconButton(
@ -249,7 +250,6 @@ fun FloconIconButton(
) {
FloconIcon(
imageVector = imageVector,
tint = tint,
)
}
}