function adapt

This commit is contained in:
2026-08-30 19:36:25 +08:00
parent bea5ead33d
commit 560f3cbac3
450 changed files with 21193 additions and 8334 deletions
+9 -3
View File
@@ -9,12 +9,17 @@ plugins {
}
kotlin {
compilerOptions {
freeCompilerArgs.add("-Xexpect-actual-classes")
}
android {
namespace = "top.zhushenwudi.llmp.composeapp"
namespace = "com.zhushenwudi.lovelivemusicplayer.composeapp"
compileSdk = libs.versions.android.compileSdk.get().toInt()
minSdk = libs.versions.android.minSdk.get().toInt()
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
freeCompilerArgs.add("-Xexpect-actual-classes")
}
androidResources {
enable = true
@@ -28,6 +33,7 @@ kotlin {
iosTarget.binaries.framework {
baseName = "ComposeApp"
isStatic = true
export(projects.shared)
}
}
@@ -41,7 +47,7 @@ kotlin {
implementation(libs.ktor.client.darwin)
}
commonMain.dependencies {
implementation(projects.shared)
api(projects.shared)
implementation(libs.compose.runtime)
implementation(libs.compose.foundation)
implementation(libs.compose.material3)
@@ -65,7 +71,7 @@ kotlin {
compose {
resources {
packageOfResClass = "top.zhushenwudi.llmp.generated.resources"
packageOfResClass = "com.zhushenwudi.lovelivemusicplayer.generated.resources"
}
}
@@ -1,4 +1,4 @@
package top.zhushenwudi.llmp.platform
package com.zhushenwudi.lovelivemusicplayer.platform
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
@@ -0,0 +1,12 @@
package com.zhushenwudi.lovelivemusicplayer.ui.image
import coil3.PlatformContext
import okio.Path
import okio.Path.Companion.toPath
import java.io.File
internal actual fun persistentImageCacheDirectory(context: PlatformContext): Path {
val dir = File(context.cacheDir, "llmp_image_cache")
if (!dir.exists()) dir.mkdirs()
return dir.absolutePath.toPath()
}
@@ -0,0 +1,22 @@
package com.zhushenwudi.lovelivemusicplayer.ui.platform
import android.content.ClipData
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.toClipEntry
import kotlinx.coroutines.launch
@Composable
actual fun rememberCopyTextHandler(): (String) -> Unit {
val clipboard = LocalClipboard.current
val scope = rememberCoroutineScope()
return { text ->
scope.launch {
clipboard.setClipEntry(
ClipData.newPlainText("text", text).toClipEntry(),
)
}
}
}
@@ -0,0 +1,11 @@
package com.zhushenwudi.lovelivemusicplayer.ui.scroll
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
@Composable
actual fun ScrollToTopHandler(listState: LazyListState) = Unit
@Composable
actual fun ScrollToTopHandler(gridState: LazyGridState) = Unit
@@ -1,29 +0,0 @@
package top.zhushenwudi.llmp.ui.player
import android.graphics.BitmapFactory
import androidx.compose.ui.graphics.Color
import androidx.palette.graphics.Palette
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
/**
* Flutter color_thief getColorFromImage → Color.fromARGB(150, r, g, b).
* Android Palette dominant is closest available substitute.
*/
actual suspend fun extractDominantColor(coverPath: String?): Color? =
withContext(Dispatchers.Default) {
if (coverPath.isNullOrBlank()) return@withContext null
val file = File(coverPath)
if (!file.isFile) return@withContext null
val bitmap = BitmapFactory.decodeFile(file.absolutePath) ?: return@withContext null
try {
val dominant = Palette.from(bitmap).generate().getDominantColor(0xFFFFAE00.toInt())
val r = android.graphics.Color.red(dominant)
val g = android.graphics.Color.green(dominant)
val b = android.graphics.Color.blue(dominant)
Color(red = r / 255f, green = g / 255f, blue = b / 255f, alpha = FlutterPaletteAlpha)
} finally {
bitmap.recycle()
}
}
@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M20 3v14a4 4 0 1 1-2-3.465V5H9v12a4 4 0 1 1-2-3.465V3h13zM5 19a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm11 0a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 319 B

After

Width:  |  Height:  |  Size: 347 B

@@ -1,14 +1,25 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<svg height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg">
<g
fill="none"
fill-rule="evenodd">
<path d="m0 0h24v24h-24z" />
<g stroke="#666" stroke-linejoin="round" stroke-width="2">
<g
stroke="#666"
stroke-linejoin="round"
stroke-width="2">
<path
d="m12 22.5c-5.7989925 0-10.5-4.7010075-10.5-10.5s4.7010075-10.5 10.5-10.5 10.5 4.7010075 10.5 10.5"
stroke-linecap="round"/>
stroke-linecap="round" />
<path d="m9.9 12v-3.637305l3.15 1.8186525 3.15 1.8186525-3.15 1.8186525-3.15 1.8186525z" />
<path
d="m9.9 12v-3.637305l3.15 1.8186525 3.15 1.8186525-3.15 1.8186525-3.15 1.8186525z" />
<path d="m18.85167 16.2v5.25" stroke-linecap="round" />
<path d="m21.45 18.798593h-5.25" stroke-linecap="round" />
d="m18.85167 16.2v5.25"
stroke-linecap="round" />
<path
d="m21.45 18.798593h-5.25"
stroke-linecap="round" />
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 691 B

After

Width:  |  Height:  |  Size: 804 B

@@ -1,5 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<path d="M2 18h10v2H2v-2zm0-7h20v2H2v-2zm0-7h20v2H2V4zm16 14v-3h2v3h3v2h-3v3h-2v-3h-3v-2h3z"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M2 18h10v2H2v-2zm0-7h20v2H2v-2zm0-7h20v2H2V4zm16 14v-3h2v3h3v2h-3v3h-2v-3h-3v-2h3z"
fill="rgba(102,102,102,1)" />
</svg>

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 304 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"
fill="rgba(153,153,153,1)" />

Before

Width:  |  Height:  |  Size: 322 B

After

Width:  |  Height:  |  Size: 350 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M17 6h5v2h-2v13a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V8H2V6h5V3a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1v3zm1 2H6v12h12V8zm-9 3h2v6H9v-6zm4 0h2v6h-2v-6zM9 4v2h6V4H9z"
fill="rgba(90,90,90,1)" />

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 365 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M16.757 3l-2 2H5v14h14V9.243l2-2V20a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h12.757zm3.728-.9L21.9 3.516l-9.192 9.192-1.412.003-.002-1.417L20.485 2.1z"
fill="rgba(90,90,90,1)" />

Before

Width:  |  Height:  |  Size: 346 B

After

Width:  |  Height:  |  Size: 374 B

@@ -1,8 +1,20 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" stroke="#666" stroke-linecap="round" stroke-linejoin="round">
<path
d="m6.15789474 2.84210526v-1.10526315c0-.40694685.32988421-.73684211.7368421-.73684211h7.36842106c.4069579 0 .7368421.32989526.7368421.73684211v8.10526315c0 .40695794-.3298842.73684214-.7368421.73684214h-1.4736842" />
<rect height="8.842105" rx="1" width="8.842105" x="1" y="5.789474" />
<svg height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg">
<g
fill="none"
fill-rule="evenodd"
stroke="#666"
stroke-linecap="round"
stroke-linejoin="round">
<path d="m6.15789474 2.84210526v-1.10526315c0-.40694685.32988421-.73684211.7368421-.73684211h7.36842106c.4069579 0 .7368421.32989526.7368421.73684211v8.10526315c0 .40695794-.3298842.73684214-.7368421.73684214h-1.4736842" />
<rect
height="8.842105"
rx="1"
width="8.842105"
x="1"
y="5.789474" />
<path d="m5.421053 8.368421v3.684211" />
<path d="m3.578947 10.210526h3.684211" />
</g>

Before

Width:  |  Height:  |  Size: 621 B

After

Width:  |  Height:  |  Size: 721 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M12 20a8 8 0 1 0 0-16 8 8 0 0 0 0 16zm0 2C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-8a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm0 2a4 4 0 1 1 0-8 4 4 0 0 1 0 8z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 368 B

After

Width:  |  Height:  |  Size: 396 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 360 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M4.828 21l-.02.02-.021-.02H2.992A.993.993 0 0 1 2 20.007V3.993A1 1 0 0 1 2.992 3h18.016c.548 0 .992.445.992.993v16.014a1 1 0 0 1-.992.993H4.828zM20 15V5H4v14L14 9l6 6zm0 2.828l-6-6L6.828 19H20v-1.172zM8 11a2 2 0 1 1 0-4 2 2 0 0 1 0 4z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 456 B

@@ -1,6 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M5 5v3h14V5H5zM4 3h16a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm2 9h6a1 1 0 0 1 1 1v3h1v6h-4v-6h1v-2H5a1 1 0 0 1-1-1v-2h2v1zm11.732 1.732l1.768-1.768 1.768 1.768a2.5 2.5 0 1 1-3.536 0z"
fill="rgba(102,102,102,1)"/>
fill="rgba(102,102,102,1)" />
</svg>

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 427 B

@@ -1,4 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="currentColor">
<path
d="M9 1V3H15V1H17V3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3H7V1H9ZM20 11H4V19H20V11ZM8 13V15H6V13H8ZM13 13V15H11V13H13ZM18 13V15H16V13H18ZM7 5H4V9H20V5H17V7H15V5H9V7H7V5Z"

Before

Width:  |  Height:  |  Size: 416 B

After

Width:  |  Height:  |  Size: 428 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M19 20H5V21C5 21.5523 4.55228 22 4 22H3C2.44772 22 2 21.5523 2 21V12L4.51334 5.29775C4.80607 4.51715 5.55231 4 6.386 4H17.614C18.4477 4 19.1939 4.51715 19.4867 5.29775L22 12V21C22 21.5523 21.5523 22 21 22H20C19.4477 22 19 21.5523 19 21V20ZM4.136 12H19.864L17.614 6H6.386L4.136 12ZM6.5 17C7.32843 17 8 16.3284 8 15.5C8 14.6716 7.32843 14 6.5 14C5.67157 14 5 14.6716 5 15.5C5 16.3284 5.67157 17 6.5 17ZM17.5 17C18.3284 17 19 16.3284 19 15.5C19 14.6716 18.3284 14 17.5 14C16.6716 14 16 14.6716 16 15.5C16 16.3284 16.6716 17 17.5 17Z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 723 B

After

Width:  |  Height:  |  Size: 751 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M12 2c5.522 0 10 3.978 10 8.889a5.558 5.558 0 0 1-5.556 5.555h-1.966c-.922 0-1.667.745-1.667 1.667 0 .422.167.811.422 1.1.267.3.434.689.434 1.122C13.667 21.256 12.9 22 12 22 6.478 22 2 17.522 2 12S6.478 2 12 2zm-1.189 16.111a3.664 3.664 0 0 1 3.667-3.667h1.966A3.558 3.558 0 0 0 20 10.89C20 7.139 16.468 4 12 4a8 8 0 0 0-.676 15.972 3.648 3.648 0 0 1-.513-1.86zM7.5 12a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm9 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM12 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 682 B

After

Width:  |  Height:  |  Size: 710 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M13 13v5.585l1.828-1.828 1.415 1.415L12 22.414l-4.243-4.242 1.415-1.415L11 18.585V13h2zM12 2a7.001 7.001 0 0 1 6.954 6.194 5.5 5.5 0 0 1-.953 10.784v-2.014a3.5 3.5 0 1 0-1.112-6.91 5 5 0 1 0-9.777 0 3.5 3.5 0 0 0-1.292 6.88l.18.03v2.014a5.5 5.5 0 0 1-.954-10.784A7 7 0 0 1 12 2z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 472 B

After

Width:  |  Height:  |  Size: 500 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M16.05 12.05L21 17l-4.95 4.95-1.414-1.414 2.536-2.537L4 18v-2h13.172l-2.536-2.536 1.414-1.414zm-8.1-10l1.414 1.414L6.828 6 20 6v2H6.828l2.536 2.536L7.95 11.95 3 7l4.95-4.95z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 367 B

After

Width:  |  Height:  |  Size: 395 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 370 B

After

Width:  |  Height:  |  Size: 398 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M13 19.9a5.002 5.002 0 0 0 4-4.9v-3a4.98 4.98 0 0 0-.415-2h-9.17A4.98 4.98 0 0 0 7 12v3a5.002 5.002 0 0 0 4 4.9V14h2v5.9zm-7.464-2.21A6.979 6.979 0 0 1 5 15H2v-2h3v-1c0-.643.087-1.265.249-1.856L3.036 8.866l1-1.732L6.056 8.3a7.01 7.01 0 0 1 .199-.3h11.49c.069.098.135.199.199.3l2.02-1.166 1 1.732-2.213 1.278c.162.59.249 1.213.249 1.856v1h3v2h-3c0 .953-.19 1.862-.536 2.69l2.5 1.444-1 1.732-2.526-1.458A6.986 6.986 0 0 1 12 22a6.986 6.986 0 0 1-5.438-2.592l-2.526 1.458-1-1.732 2.5-1.443zM8 6a4 4 0 1 1 8 0H8z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 702 B

After

Width:  |  Height:  |  Size: 730 B

@@ -1,5 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="currentColor">
<path
d="M20.4668 8.69379L20.7134 8.12811C21.1529 7.11947 21.9445 6.31641 22.9323 5.87708L23.6919 5.53922C24.1027 5.35653 24.1027 4.75881 23.6919 4.57612L22.9748 4.25714C21.9616 3.80651 21.1558 2.97373 20.7238 1.93083L20.4706 1.31953C20.2942 0.893489 19.7058 0.893489 19.5293 1.31953L19.2761 1.93083C18.8442 2.97373 18.0384 3.80651 17.0252 4.25714L16.308 4.57612C15.8973 4.75881 15.8973 5.35653 16.308 5.53922L17.0677 5.87708C18.0555 6.31641 18.8471 7.11947 19.2866 8.12811L19.5331 8.69379C19.7136 9.10792 20.2864 9.10792 20.4668 8.69379ZM2 4C2 3.44772 2.44772 3 3 3H14V5H4V19H20V11H22V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4ZM12 12.1707V6H16V8H14V15C14 16.6569 12.6569 18 11 18C9.34315 18 8 16.6569 8 15C8 13.3431 9.34315 12 11 12C11.3506 12 11.6872 12.0602 12 12.1707Z" />
<path d="M20.4668 8.69379L20.7134 8.12811C21.1529 7.11947 21.9445 6.31641 22.9323 5.87708L23.6919 5.53922C24.1027 5.35653 24.1027 4.75881 23.6919 4.57612L22.9748 4.25714C21.9616 3.80651 21.1558 2.97373 20.7238 1.93083L20.4706 1.31953C20.2942 0.893489 19.7058 0.893489 19.5293 1.31953L19.2761 1.93083C18.8442 2.97373 18.0384 3.80651 17.0252 4.25714L16.308 4.57612C15.8973 4.75881 15.8973 5.35653 16.308 5.53922L17.0677 5.87708C18.0555 6.31641 18.8471 7.11947 19.2866 8.12811L19.5331 8.69379C19.7136 9.10792 20.2864 9.10792 20.4668 8.69379ZM2 4C2 3.44772 2.44772 3 3 3H14V5H4V19H20V11H22V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4ZM12 12.1707V6H16V8H14V15C14 16.6569 12.6569 18 11 18C9.34315 18 8 16.6569 8 15C8 13.3431 9.34315 12 11 12C11.3506 12 11.6872 12.0602 12 12.1707Z" />
</svg>

Before

Width:  |  Height:  |  Size: 922 B

After

Width:  |  Height:  |  Size: 926 B

@@ -1,5 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M3.91762 8.0366C3.32984 9.23328 3 10.5792 3 11.9999C3 16.9704 7.02944 20.9999 12 20.9999C13.4216 20.9999 14.7684 20.6696 15.9657 20.081C16.8385 20.4544 17.6848 20.6991 18.4564 20.762C19.3582 20.8356 20.3 20.6665 20.9818 19.9847C21.7339 19.2325 21.8625 18.1689 21.7279 17.1727C21.6052 16.2638 21.2481 15.2537 20.726 14.2114C20.9051 13.503 21 12.7619 21 11.9999C21 7.02929 16.9706 2.99986 12 2.99986C11.2389 2.99986 10.4987 3.09454 9.79103 3.27318C8.7474 2.7498 7.73605 2.39172 6.8261 2.26834C5.82897 2.13315 4.76406 2.26128 4.01121 3.01413C3.3287 3.69664 3.16001 4.63956 3.2341 5.54233C3.29752 6.315 3.54313 7.16247 3.91762 8.0366ZM5.3224 5.96574C5.2734 5.75321 5.24204 5.55715 5.2274 5.37873C5.17928 4.79243 5.31727 4.53649 5.42543 4.42834C5.54452 4.30925 5.84797 4.15403 6.55739 4.25021C6.75482 4.27698 6.96951 4.32189 7.2 4.38547C6.50364 4.82527 5.87203 5.35784 5.3224 5.96574ZM19.6124 16.803C19.6751 17.0316 19.7195 17.2445 19.7459 17.4404C19.8416 18.1485 19.6865 18.4515 19.5676 18.5705C19.4595 18.6785 19.204 18.8163 18.6189 18.7686C18.4419 18.7542 18.2475 18.7233 18.0368 18.675C18.6427 18.1268 19.1736 17.4971 19.6124 16.803ZM15.8812 17.8264C14.2046 16.9482 12.2571 15.5026 10.3752 13.6207C8.4954 11.741 7.05092 9.79561 6.17241 8.12028C7.06357 6.78465 8.40124 5.77312 9.96933 5.29866C10.6108 5.10457 11.2923 4.99986 12 4.99986C15.866 4.99986 19 8.13386 19 11.9999C19 12.7083 18.8951 13.3904 18.7006 14.0325C18.2261 15.5991 17.2155 16.9356 15.8812 17.8264ZM13.6052 18.8152C13.0901 18.9359 12.5528 18.9999 12 18.9999C8.13401 18.9999 5 15.8658 5 11.9999C5 11.4476 5.06377 10.9109 5.18429 10.3963C6.14883 11.913 7.43475 13.5087 8.96096 15.0349C10.489 16.563 12.0868 17.8502 13.6052 18.8152Z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M18.031 16.617l4.283 4.282-1.415 1.415-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9 9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617zm-2.006-.742A6.977 6.977 0 0 0 18 11c0-3.868-3.133-7-7-7-3.868 0-7 3.132-7 7 0 3.867 3.132 7 7 7a6.977 6.977 0 0 0 4.875-1.975l.15-.15zm-3.847-8.699a2 2 0 1 0 2.646 2.646 4 4 0 1 1-2.646-2.646z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 533 B

After

Width:  |  Height:  |  Size: 561 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M4.929 2.929l1.414 1.414A7.975 7.975 0 0 0 4 10c0 2.21.895 4.21 2.343 5.657L4.93 17.07A9.969 9.969 0 0 1 2 10a9.969 9.969 0 0 1 2.929-7.071zm14.142 0A9.969 9.969 0 0 1 22 10a9.969 9.969 0 0 1-2.929 7.071l-1.414-1.414A7.975 7.975 0 0 0 20 10c0-2.21-.895-4.21-2.343-5.657L19.07 2.93zM7.757 5.757l1.415 1.415A3.987 3.987 0 0 0 8 10c0 1.105.448 2.105 1.172 2.828l-1.415 1.415A5.981 5.981 0 0 1 6 10c0-1.657.672-3.157 1.757-4.243zm8.486 0A5.981 5.981 0 0 1 18 10a5.981 5.981 0 0 1-1.757 4.243l-1.415-1.415A3.987 3.987 0 0 0 16 10a3.987 3.987 0 0 0-1.172-2.828l1.415-1.415zM12 12a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 2c.58 0 1.077.413 1.184.983L14.5 22h-5l1.316-7.017c.107-.57.604-.983 1.184-.983z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 879 B

After

Width:  |  Height:  |  Size: 907 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 352 B

After

Width:  |  Height:  |  Size: 380 B

@@ -1,5 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="currentColor">
<path
d="M6 7.82843V20H18V4H9.82843L6 7.82843ZM4.29289 6.70711L9 2H19C19.5523 2 20 2.44772 20 3V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V7.41421C4 7.149 4.10536 6.89464 4.29289 6.70711ZM15 5H17V9H15V5ZM12 5H14V9H12V5ZM9 6H11V9H9V6Z" />
<path d="M6 7.82843V20H18V4H9.82843L6 7.82843ZM4.29289 6.70711L9 2H19C19.5523 2 20 2.44772 20 3V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V7.41421C4 7.149 4.10536 6.89464 4.29289 6.70711ZM15 5H17V9H15V5ZM12 5H14V9H12V5ZM9 6H11V9H9V6Z" />
</svg>

Before

Width:  |  Height:  |  Size: 384 B

After

Width:  |  Height:  |  Size: 390 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM11 7h2v2h-2V7zm0 4h2v6h-2v-6z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 332 B

After

Width:  |  Height:  |  Size: 360 B

@@ -1,6 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M2 12c0-.865.11-1.703.316-2.504A3 3 0 0 0 4.99 4.867a9.99 9.99 0 0 1 4.335-2.505 3 3 0 0 0 5.348 0 9.99 9.99 0 0 1 4.335 2.505 3 3 0 0 0 2.675 4.63c.206.8.316 1.638.316 2.503 0 .865-.11 1.703-.316 2.504a3 3 0 0 0-2.675 4.629 9.99 9.99 0 0 1-4.335 2.505 3 3 0 0 0-5.348 0 9.99 9.99 0 0 1-4.335-2.505 3 3 0 0 0-2.675-4.63C2.11 13.704 2 12.866 2 12zm4.804 3c.63 1.091.81 2.346.564 3.524.408.29.842.541 1.297.75A4.993 4.993 0 0 1 12 18c1.26 0 2.438.471 3.335 1.274.455-.209.889-.46 1.297-.75A4.993 4.993 0 0 1 17.196 15a4.993 4.993 0 0 1 2.77-2.25 8.126 8.126 0 0 0 0-1.5A4.993 4.993 0 0 1 17.195 9a4.993 4.993 0 0 1-.564-3.524 7.989 7.989 0 0 0-1.297-.75A4.993 4.993 0 0 1 12 6a4.993 4.993 0 0 1-3.335-1.274 7.99 7.99 0 0 0-1.297.75A4.993 4.993 0 0 1 6.804 9a4.993 4.993 0 0 1-2.77 2.25 8.126 8.126 0 0 0 0 1.5A4.993 4.993 0 0 1 6.805 15zM12 15a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0-2a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"
fill="rgba(102,102,102,1)"/>
fill="rgba(102,102,102,1)" />
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,6 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M11 2.05v2.012A8.001 8.001 0 0 0 12 20a8.001 8.001 0 0 0 7.938-7h2.013c-.502 5.053-4.766 9-9.951 9-5.523 0-10-4.477-10-10 0-5.185 3.947-9.449 9-9.95zm7.707 4.657L12 13.414 10.586 12l6.707-6.707L14 2h8v8l-3.293-3.293z"
fill="rgba(102,102,102,1)"/>
fill="rgba(102,102,102,1)" />
</svg>

Before

Width:  |  Height:  |  Size: 409 B

After

Width:  |  Height:  |  Size: 438 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M14.515 5l2.606-2.607a1 1 0 0 1 1.415 0l4.242 4.243a1 1 0 0 1 0 1.414L19 11.828V21a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-9.172L1.222 8.05a1 1 0 0 1 0-1.414l4.242-4.243a1 1 0 0 1 1.415 0L9.485 5h5.03zm.828 2H8.657L6.172 4.515 3.343 7.343 7 11v9h10v-9l3.657-3.657-2.829-2.828L15.343 7z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 470 B

After

Width:  |  Height:  |  Size: 498 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM13 12H17V14H11V7H13V12Z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 444 B

After

Width:  |  Height:  |  Size: 472 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M12 12.586l4.243 4.242-1.415 1.415L13 16.415V22h-2v-5.587l-1.828 1.83-1.415-1.415L12 12.586zM12 2a7.001 7.001 0 0 1 6.954 6.194 5.5 5.5 0 0 1-.953 10.784v-2.014a3.5 3.5 0 1 0-1.112-6.91 5 5 0 1 0-9.777 0 3.5 3.5 0 0 0-1.292 6.88l.18.03v2.014a5.5 5.5 0 0 1-.954-10.784A7 7 0 0 1 12 2z"
fill="rgba(102,102,102,1)" />

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 505 B

@@ -1,4 +1,7 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<svg id="Layer_1"
data-name="Layer 1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<path
d="M265.71,312.6a9.62,9.62,0,0,1-8.6-5.31l-39.48-79L178.24,306a9.61,9.61,0,0,1-17.14-8.69l48-94.72a9.61,9.61,0,0,1,8.57-5.26h0a9.59,9.59,0,0,1,8.56,5.31l48,96a9.61,9.61,0,0,1-8.58,13.9Z"
style="fill:#f15a24" />

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -1,4 +1,7 @@
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<svg id="Layer_2"
data-name="Layer 2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<path
d="M750.1,313c-10.07,0-20.35-3-27.91-8.84a9.92,9.92,0,0,1,12.07-15.74c7.36,5.64,19.41,5.92,26,2.65,4.28-2.11,4.28-4.52,4.28-5.42v-.09c-1.2-2.12-9.81-5-14.44-6.52-12.31-4.1-29.16-9.7-30.37-26.62-.84-11.77,5.32-21.85,16.48-27,14.08-6.46,32.2-3.32,43.07,7.47a9.92,9.92,0,1,1-14,14.08c-5.64-5.6-15-6.19-20.82-3.52-5.33,2.44-5.06,6.13-5,7.52.26,3.7,9.45,6.76,16.84,9.22,11.82,3.93,28,9.32,28,25.43,0,9.94-5.6,18.4-15.35,23.21A43.19,43.19,0,0,1,750.1,313Z"
style="fill:#e4007f" />
@@ -23,7 +26,11 @@
<path
d="M443.54,330.55a9.92,9.92,0,0,1-9.92-9.91V269.07a9.92,9.92,0,1,1,19.84,0v51.57A9.92,9.92,0,0,1,443.54,330.55Z"
style="fill:#e4007f" />
<circle cx="443.54" cy="347.08" r="9.92" style="fill:#29abe2" />
<circle
cx="443.54"
cy="347.08"
r="9.92"
style="fill:#29abe2" />
<path
d="M346.69,314a9.92,9.92,0,0,1-8.88-5.49L298,229l-39.68,78.3a9.92,9.92,0,0,1-17.7-9l48.6-95.87a9.92,9.92,0,0,1,8.84-5.43h0a9.93,9.93,0,0,1,8.85,5.48l48.59,97.19A9.92,9.92,0,0,1,346.69,314Z"
style="fill:#e4007f" />

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -1,24 +1,44 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<path d="M221,240a32,32,0,1,1-32,32"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<path
d="M221,240a32,32,0,1,1-32,32"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<line x1="189" y1="208" x2="189" y2="272"
<line
x1="189"
y1="208"
x2="189"
y2="272"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<path d="M835,208v64a32,32,0,1,1-32-32"
<path
d="M835,208v64a32,32,0,1,1-32-32"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<path d="M574.14,240a32,32,0,1,1-32,32"
<path
d="M574.14,240a32,32,0,1,1-32,32"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<line x1="542.14" y1="208" x2="542.14" y2="272"
<line
x1="542.14"
y1="208"
x2="542.14"
y2="272"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<path d="M289.29,208v64a32,32,0,0,0,32,32"
<path
d="M289.29,208v64a32,32,0,0,0,32,32"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<path d="M643.43,240v32a32,32,0,0,0,32,32"
<path
d="M643.43,240v32a32,32,0,0,0,32,32"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<path d="M711.71,304V272a32,32,0,0,1,32-32"
<path
d="M711.71,304V272a32,32,0,0,1,32-32"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<path d="M412.57,240v32a32,32,0,0,1-64,0V240"
<path
d="M412.57,240v32a32,32,0,0,1-64,0V240"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-miterlimit:10;stroke-width:19px" />
<path
d="M496.93,247.13a32,32,0,1,0-7.48,54.26,32.8,32.8,0,0,0,12.14-9.17A37.69,37.69,0,0,0,510,274.35"
style="fill:none;stroke:#007ebc;stroke-linecap:round;stroke-linejoin:round;stroke-width:19px" />
<circle cx="478.5" cy="271.5" r="9" style="fill:aqua" />
<circle
cx="478.5"
cy="271.5"
r="9"
style="fill:aqua" />
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,4 +1,7 @@
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<svg id="Layer_2"
data-name="Layer 2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<path
d="M180.44,303.83q-5.4,7.44-10.32,3.24t1.56-10.68a30.77,30.77,0,0,1,8.76-5.76,2.47,2.47,0,0,0,1.56-2.4V268a.53.53,0,0,0-.6-.6h-3.48q-9,0-9-6t9-6h8.28q9,0,9,9v24.24a3.49,3.49,0,0,0,1.68,3.12l1.2.72q3.72,2.4,7.56,4.68,1.56,1,10,2.16a128.88,128.88,0,0,0,17.88,1q25.44,0,34.44-1.2t9.6,4.8q.48,6.12-8.64,6.36l-31.2.84q-4.56.12-9.12.12A88.17,88.17,0,0,1,214,310c-5.44-.89-8.76-1.68-10-2.4Q200,305.27,198,304t-5.88-4.08a5.91,5.91,0,0,0-3.48-1.2Q184.28,298.67,180.44,303.83Zm63.48-78.36q-7.08,0-7.08-6.48a.53.53,0,0,0-.6-.6H210a.53.53,0,0,0-.6.6q0,6.84-7.08,6.84T195.2,219a.53.53,0,0,0-.6-.6H178.88q-9,0-9-5.88t9-5.76H194.6a.53.53,0,0,0,.6-.6q0-5.64,7.08-5.64t7.08,5.64a.53.53,0,0,0,.6.6h26.28a.53.53,0,0,0,.6-.6q0-5.64,7.08-5.64t7.08,5.64a.53.53,0,0,0,.6.6h16.32q9,0,9,5.76t-9,5.88H251.6a.53.53,0,0,0-.6.6Q251,225.47,243.92,225.47Zm-53.28,18.6q-5.76,3.72-7.74.54a21.18,21.18,0,0,0-4.86-5.46q-7-5.52-3.24-10.32t10.92.24a34.36,34.36,0,0,1,8.1,7.62Q196.28,240.23,190.64,244.07Zm46.44,53.16q-6.6,0-6.6-5.4a.53.53,0,0,0-.6-.6h-21q-9,0-9-5t9-5h21a.53.53,0,0,0,.6-.6v-3.36a.53.53,0,0,0-.6-.6h-15.6q-9,0-9-9v-16.2q0-9,9-9h15.6a.53.53,0,0,0,.6-.6v-3.12a.53.53,0,0,0-.6-.6H210.8q-9,0-9-4.8t9-4.68h19.08a.53.53,0,0,0,.6-.6q0-4.68,6.6-4.68t6.6,4.68a.53.53,0,0,0,.6.6H264.8q9,0,9,4.68t-9,4.8H244.28a.53.53,0,0,0-.6.6v3.12a.53.53,0,0,0,.6.6H261q9,0,9,9v16.2q0,9-9,9H244.28a.53.53,0,0,0-.6.6v3.36a.53.53,0,0,0,.6.6h22.2q9,0,9,5t-9,5h-22.2a.53.53,0,0,0-.6.6Q243.68,297.23,237.08,297.23Zm-18.84-42.36a.53.53,0,0,0,.6.6h11a.53.53,0,0,0,.6-.6v-3.48a.53.53,0,0,0-.6-.6h-11a.53.53,0,0,0-.6.6Zm0,12.72a.53.53,0,0,0,.6.6h11a.53.53,0,0,0,.6-.6V264a.53.53,0,0,0-.6-.6h-11a.53.53,0,0,0-.6.6Zm25.44-12.72a.53.53,0,0,0,.6.6H255.8a.53.53,0,0,0,.6-.6v-3.48a.53.53,0,0,0-.6-.6H244.28a.53.53,0,0,0-.6.6Zm0,12.72a.53.53,0,0,0,.6.6H255.8a.53.53,0,0,0,.6-.6V264a.53.53,0,0,0-.6-.6H244.28a.53.53,0,0,0-.6.6Z"
style="fill:#e4007f" />

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

@@ -1,9 +1,20 @@
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<circle cx="726.68" cy="303.29" r="10.35" style="fill:#939" />
<svg id="Layer_2"
data-name="Layer 2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<circle
cx="726.68"
cy="303.29"
r="10.35"
style="fill:#939" />
<path
d="M726.68,286.72a10.36,10.36,0,0,1-10.36-10.36V208.71a10.36,10.36,0,0,1,20.71,0v67.65A10.36,10.36,0,0,1,726.68,286.72Z"
style="fill:#e4007f" />
<circle cx="406.27" cy="208.71" r="10.35" style="fill:#939" />
<circle
cx="406.27"
cy="208.71"
r="10.35"
style="fill:#939" />
<path
d="M406.27,225.28a10.36,10.36,0,0,0-10.35,10.36v67.65a10.36,10.36,0,0,0,20.71,0V235.64A10.36,10.36,0,0,0,406.27,225.28Z"
style="fill:#e4007f" />

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,5 +1,9 @@
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<path d="M302.78,228H248.72a9.32,9.32,0,1,1,0-18.63h54.06a9.32,9.32,0,1,1,0,18.63Z"
<svg id="Layer_2"
data-name="Layer 2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<path
d="M302.78,228H248.72a9.32,9.32,0,1,1,0-18.63h54.06a9.32,9.32,0,1,1,0,18.63Z"
style="fill:#e4007f" />
<path
d="M261.06,312a9.32,9.32,0,1,1,0-18.63c14.71,0,25.82-5.82,33-17.31a56.11,56.11,0,0,0,7.27-19.3H237a9.32,9.32,0,0,1,0-18.63H311.6a9.31,9.31,0,0,1,9.31,9.31c0,10.11-2.9,25.54-11,38.52C302.42,297.84,287.9,312,261.06,312Z"
@@ -7,19 +11,40 @@
<path
d="M359.2,307.3a9.32,9.32,0,0,1,0-18.63c11.45,0,45.07,0,48.57-53.07h-65a9.32,9.32,0,1,1,0-18.63h74.63a9.32,9.32,0,0,1,9.32,9.32C426.7,278.53,402.73,307.3,359.2,307.3Z"
style="fill:#e4007f" />
<path d="M519.64,228H465.57a9.32,9.32,0,0,1,0-18.63h54.07a9.32,9.32,0,0,1,0,18.63Z"
<path
d="M519.64,228H465.57a9.32,9.32,0,0,1,0-18.63h54.07a9.32,9.32,0,0,1,0,18.63Z"
style="fill:#e4007f" />
<path
d="M477.91,312a9.32,9.32,0,0,1,0-18.63c14.72,0,25.83-5.82,33-17.31a56.11,56.11,0,0,0,7.27-19.3H453.82a9.32,9.32,0,1,1,0-18.63h74.63a9.31,9.31,0,0,1,9.32,9.31c0,10.11-2.9,25.54-11,38.52C519.27,297.84,504.76,312,477.91,312Z"
style="fill:#e4007f" />
<circle cx="414.44" cy="204.91" r="9.31" style="fill:#ff0073" />
<circle cx="436.32" cy="201.31" r="9.31" style="fill:#ff0073" />
<circle
cx="414.44"
cy="204.91"
r="9.31"
style="fill:#ff0073" />
<circle
cx="436.32"
cy="201.31"
r="9.31"
style="fill:#ff0073" />
<path
d="M680.08,307.3a9.32,9.32,0,1,1,0-18.63c11.45,0,45.07,0,48.57-53.07h-65a9.32,9.32,0,0,1,0-18.63h74.64a9.31,9.31,0,0,1,9.31,9.32C747.57,278.53,723.6,307.3,680.08,307.3Z"
style="fill:#e4007f" />
<circle cx="735.32" cy="204.91" r="9.31" style="fill:#ff0073" />
<circle cx="757.2" cy="201.31" r="9.31" style="fill:#ff0073" />
<circle cx="787.04" cy="298.48" r="9.31" style="fill:#ff0073" />
<circle
cx="735.32"
cy="204.91"
r="9.31"
style="fill:#ff0073" />
<circle
cx="757.2"
cy="201.31"
r="9.31"
style="fill:#ff0073" />
<circle
cx="787.04"
cy="298.48"
r="9.31"
style="fill:#ff0073" />
<path
d="M557.25,271.45a9.32,9.32,0,0,1,0-18.63c10.23,0,26-5.85,41.06-15.26,14.55-9.08,26-19.94,29-27.66a9.32,9.32,0,1,1,17.3,6.92C634.85,241.32,586.66,271.45,557.25,271.45Z"
style="fill:#e4007f" />

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -1,4 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<path
d="M690.21,371.43a9.6,9.6,0,0,1-8.59-5.31L656.54,316l-25,49.29a9.6,9.6,0,1,1-17.13-8.68L648,290.26A9.61,9.61,0,0,1,656.6,285h0a9.59,9.59,0,0,1,8.56,5.31l33.62,67.22a9.62,9.62,0,0,1-8.59,13.9Z"
style="fill:#e4007f" />
@@ -50,9 +51,29 @@
<path
d="M237.51,257.81c-21.23,0-38.51-16.15-38.51-36v-14c0-19.85,17.28-36,38.51-36a39.81,39.81,0,0,1,26.64,10A9.5,9.5,0,1,1,251.48,196a20.84,20.84,0,0,0-14-5.15c-10.76,0-19.51,7.63-19.51,17v14c0,9.37,8.75,17,19.51,17a20.93,20.93,0,0,0,14.42-5.57A9.5,9.5,0,1,1,265.07,247,39.59,39.59,0,0,1,237.51,257.81Z"
style="fill:#e4007f" />
<circle cx="317.5" cy="224.5" r="9.5" style="fill:#f15a24" />
<circle cx="732.5" cy="317.5" r="9.5" style="fill:#f15a24" />
<circle cx="646.5" cy="165.5" r="9.5" style="fill:#e4007f" />
<circle cx="119.5" cy="150.5" r="9.5" style="fill:#e4007f" />
<circle cx="273.5" cy="366.5" r="9.5" style="fill:#e4007f" />
<circle
cx="317.5"
cy="224.5"
r="9.5"
style="fill:#f15a24" />
<circle
cx="732.5"
cy="317.5"
r="9.5"
style="fill:#f15a24" />
<circle
cx="646.5"
cy="165.5"
r="9.5"
style="fill:#e4007f" />
<circle
cx="119.5"
cy="150.5"
r="9.5"
style="fill:#e4007f" />
<circle
cx="273.5"
cy="366.5"
r="9.5"
style="fill:#e4007f" />
</svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

@@ -1,4 +1,7 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<svg id="Layer_1"
data-name="Layer 1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<path
d="M158.59,233.92q-9,0-9-4.44,0-.6-.6-.36-4.92,1.56-5.76-3.36a.54.54,0,0,0-.72-.48L108.43,231q-8.88,1.56-10-5.4t7.92-8L118.27,216a1,1,0,0,0,1-1.08V195.88a.53.53,0,0,0-.6-.6h-5.52a.53.53,0,0,0-.6.6q0,4.08-5.64,4.08t-5.76-9v-32.4q0-9,9-9h8.64a.53.53,0,0,0,.6-.6V137.44q0-9,6.36-9t6.36,9V149a.53.53,0,0,0,.6.6h8q9,0,9,9v27.72q0,9-9,9h-8.16a.53.53,0,0,0-.6.6V213.4q0,.6.6.48l7-.84a.54.54,0,0,0,.48-.72l-.24-.6q-2.64-8.52,2.76-10.32t8.4,6.6q.48,1.32,2.88,11a1.34,1.34,0,0,0,1.44,1.2h16.32a.53.53,0,0,0,.6-.6v-67.2a.53.53,0,0,0-.6-.6H161q-9,0-9-6.84t9-6.84h36.84q9,0,9,6.84t-9,6.84h-10a.53.53,0,0,0-.6.6v67.2a.53.53,0,0,0,.6.6h12.36q9,0,9,6.84t-9,6.84ZM112.51,183a.53.53,0,0,0,.6.6H120a.53.53,0,0,0,.6-.6V161.8a.53.53,0,0,0-.6-.6h-6.84a.53.53,0,0,0-.6.6Zm18.24,0a.53.53,0,0,0,.6.6H138a.53.53,0,0,0,.6-.6V161.8a.53.53,0,0,0-.6-.6h-6.6a.53.53,0,0,0-.6.6Z"
style="fill:#f7931e" />
@@ -38,7 +41,9 @@
<path
d="M662.35,384q-7.32.36-7.32-6.72t3.66-6.84q3.54.24,4.38.24,4.56-.36,6-.42t1.44-2.7V291.88a.53.53,0,0,0-.6-.6h-72.6a.53.53,0,0,0-.6.6v83.28q0,9-7,9t-7-9V286.72q0-9,9-9h83.76q9,0,9,9v81a22.73,22.73,0,0,1-1.38,8.82,10,10,0,0,1-4.68,4.92,22.47,22.47,0,0,1-4,1.86q-4.68.48-9.36.6Z"
style="fill:#e4007f" />
<path d="M612.55,311.68c-6,0-9-2-9-6s3-6,9-6h41.88q9,0,9,6c0,4-3,6-9,6Z" style="fill:#f7931e" />
<path
d="M612.55,311.68c-6,0-9-2-9-6s3-6,9-6h41.88q9,0,9,6c0,4-3,6-9,6Z"
style="fill:#f7931e" />
<path
d="M615.79,368.92q-6.6,0-6.6-9v-30q0-9,9-9H649q9,0,9,9V352q0,9-9,9H623a.53.53,0,0,0-.6.6Q622.39,368.92,615.79,368.92Zm6.6-20.28a.53.53,0,0,0,.6.6h21.24a.53.53,0,0,0,.6-.6V333.28a.53.53,0,0,0-.6-.6H623a.53.53,0,0,0-.6.6Z"
style="fill:#e4007f" />
@@ -57,12 +62,16 @@
<path
d="M251,361.92q-8.52,3.24-10.56-2.64c-1.44-3.84-.84-6.21,1.8-7.14s5.32-3.33,8-7.26q3.48-5.14,4.44-14.52a.53.53,0,0,0-.6-.6h-5.52c-6,0-9-1.95-9-5.88s3-5.76,9-5.76H297q9,0,9,5.76c0,3.93-3,5.88-9,5.88h-28.8a.88.88,0,0,0-1,.85q-1.08,12.94-5.64,20.51Q256.23,359.88,251,361.92Z"
style="fill:#e4007f" />
<path d="M514.05,278.62c.5.5-12,21-23,26"
<path
d="M514.05,278.62c.5.5-12,21-23,26"
style="fill:none;stroke:#f7931e;stroke-linecap:round;stroke-miterlimit:10;stroke-width:12px" />
<path d="M528.05,302.62c7.5-6.5,14-17,13.5-17.5s-30,.74-30,.74"
<path
d="M528.05,302.62c7.5-6.5,14-17,13.5-17.5s-30,.74-30,.74"
style="fill:none;stroke:#f7931e;stroke-linecap:round;stroke-miterlimit:10;stroke-width:12px" />
<path d="M755.55,285.12h44c0,6.16-20.8,28-23.5,29.5"
<path
d="M755.55,285.12h44c0,6.16-20.8,28-23.5,29.5"
style="fill:none;stroke:#f7931e;stroke-linecap:round;stroke-linejoin:round;stroke-width:13px" />
<path d="M854.11,321.32c-6,0-9-2.19-9-6.6s3-6.6,9-6.6H896q9,0,9,6.6c0,4.41-3,6.6-9,6.6Z"
<path
d="M854.11,321.32c-6,0-9-2.19-9-6.6s3-6.6,9-6.6H896q9,0,9,6.6c0,4.41-3,6.6-9,6.6Z"
style="fill:#f7931e" />
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@@ -1,4 +1,7 @@
<svg id="Layer_3" data-name="Layer 3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<svg id="Layer_3"
data-name="Layer 3"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<path
d="M432,336a11.14,11.14,0,0,1-11.14-11.14V217.21a11.14,11.14,0,1,1,22.28,0V324.86A11.14,11.14,0,0,1,432,336Z"
style="fill:#e4007f" />
@@ -8,5 +11,9 @@
<path
d="M564.59,308.13c-11.31,0-22.86-3.42-31.34-9.92a11.14,11.14,0,0,1,13.55-17.68c8.26,6.34,21.8,6.65,29.25,3,4.81-2.37,4.81-5.07,4.81-6.08v-.11c-1.35-2.37-11-5.59-16.22-7.32-13.82-4.59-32.74-10.89-34.1-29.89-.94-13.22,6-24.54,18.5-30.28,15.82-7.26,36.16-3.73,48.37,8.39A11.13,11.13,0,1,1,581.72,234c-6.33-6.28-16.86-6.94-23.39-3.95-6,2.74-5.68,6.89-5.57,8.45.3,4.16,10.62,7.59,18.91,10.35,13.28,4.41,31.47,10.46,31.47,28.56,0,11.16-6.28,20.66-17.23,26.06A48.6,48.6,0,0,1,564.59,308.13Z"
style="fill:#e4007f" />
<circle cx="516.45" cy="187.14" r="11.14" style="fill:#f7931e" />
<circle
cx="516.45"
cy="187.14"
r="11.14"
style="fill:#f7931e" />
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,4 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<g id="Layer_4">
<path
d="M245.14,311.41q-7.63,1.48-8-.47a107.26,107.26,0,0,0-3.29-10.52,1.29,1.29,0,0,0-1.47-.94L203.06,305a80.6,80.6,0,0,0-9.25,2.15q-6,1.61-8.85-5.9t1.88-10.86a45.58,45.58,0,0,0,3.35-3.62l3.88-4.56q7.38-8.56,18.1-27.61c.18-.35,0-.49-.4-.4l-15.42,2.14q-9.24,1.35-12.19-6.16t3.21-13.67a72,72,0,0,0,5.5-6.3q.4-.54,6.16-11.8,6.84-13.27,7.38-15,3.34-9.65,11.39-6.57t4,12.47q-5,11.52-18.23,30.56c-.27.36-.18.53.27.53l14.34-.93a3.45,3.45,0,0,0,3.08-2,69.36,69.36,0,0,0,3.56-7.84c.75-2.09,3.82-2,9.18.34s6.39,6.52,3.35,12.6Q227.32,263,208,288c-.26.36-.18.49.27.4l19.17-2.82c.45-.08.58-.35.4-.8L226,280.59q-3.9-9.12,2.41-12.07t11,5.77q9.38,18.36,11,25.73Q252.65,310,245.14,311.41Zm20.91,3.76q-10.05-.54-9.51-8.31c.35-5.1,1.34-7.58,2.95-7.44s5.89.2,12.86.2c2.33,0,3.8-.4,4.43-1.21s1.4-1.76,2.34-2.88,1.9-7.44,2.88-19q1.35-16.21,2.28-54.82a.59.59,0,0,0-.67-.67H249q-10,0-10.05-7.64T249,205.79h41.42q10.05,0,9.78,10.05-.93,47.6-2.41,65.28-1.74,20.38-2.55,21.45c-2,2.68-3.37,4.64-4.22,5.9a8.66,8.66,0,0,1-2.81,2.74q-1.55.87-2.88,1.68a20.2,20.2,0,0,1-5.63,1.61A62.41,62.41,0,0,1,266.05,315.17Z"

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

@@ -1,4 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 512">
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 512">
<g id="Layer_4">
<path
d="M245.14,311.41q-7.63,1.48-8-.47a107.26,107.26,0,0,0-3.29-10.52,1.29,1.29,0,0,0-1.47-.94L203.06,305a80.6,80.6,0,0,0-9.25,2.15q-6,1.61-8.85-5.9t1.88-10.86a45.58,45.58,0,0,0,3.35-3.62l3.88-4.56q7.38-8.56,18.1-27.61c.18-.35,0-.49-.4-.4l-15.42,2.14q-9.24,1.35-12.19-6.16t3.21-13.67a72,72,0,0,0,5.5-6.3q.4-.54,6.16-11.8,6.84-13.27,7.38-15,3.34-9.65,11.39-6.57t4,12.47q-5,11.52-18.23,30.56c-.27.36-.18.53.27.53l14.34-.93a3.45,3.45,0,0,0,3.08-2,69.36,69.36,0,0,0,3.56-7.84c.75-2.09,3.82-2,9.18.34s6.39,6.52,3.35,12.6Q227.32,263,208,288c-.26.36-.18.49.27.4l19.17-2.82c.45-.08.58-.35.4-.8L226,280.59q-3.9-9.12,2.41-12.07t11,5.77q9.38,18.36,11,25.73Q252.65,310,245.14,311.41Zm20.91,3.76q-10.05-.54-9.51-8.31c.35-5.1,1.34-7.58,2.95-7.44s5.89.2,12.86.2c2.33,0,3.8-.4,4.43-1.21s1.4-1.76,2.34-2.88,1.9-7.44,2.88-19q1.35-16.21,2.28-54.82a.59.59,0,0,0-.67-.67H249q-10,0-10.05-7.64T249,205.79h41.42q10.05,0,9.78,10.05-.93,47.6-2.41,65.28-1.74,20.38-2.55,21.45c-2,2.68-3.37,4.64-4.22,5.9a8.66,8.66,0,0,1-2.81,2.74q-1.55.87-2.88,1.68a20.2,20.2,0,0,1-5.63,1.61A62.41,62.41,0,0,1,266.05,315.17Z"

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

@@ -1,13 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="42px" height="42px" viewBox="0 0 42 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="42px"
height="42px"
viewBox="0 0 42 42"
version="1.1"
xmlns="http://www.w3.org/2000/svg">
<title>编组 4</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="歌库-播放详情页-车载版-暗色备份" transform="translate(-60.000000, -548.000000)">
<g id="编组-4" transform="translate(60.000000, 548.000000)">
<rect id="矩形备份-6" x="0" y="0" width="42" height="42" rx="6"></rect>
<g id="编组" transform="translate(7.016512, 9.508856)">
<rect id="矩形" fill-opacity="0.01" fill="#FFFFFF" fill-rule="nonzero" x="3.48348783" y="0.99114375" width="21" height="21"></rect>
<path d="M7.69091831,0 C3.44334299,0 0,3.44336396 0,7.69091831 C0,15.3818366 9.08926709,22.3735805 13.9834878,24 C18.8777086,22.3735805 27.9669757,15.3818366 27.9669757,7.69091831 C27.9669757,3.44336396 24.5236117,0 20.2760574,0 C17.6749189,0 15.3753343,1.29130518 13.9834878,3.26780127 C12.5916414,1.29130518 10.2920568,0 7.69091831,0 Z" id="路径" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path>
<g
id="页面-1"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd">
<g
id="歌库-播放详情页-车载版-暗色备份"
transform="translate(-60.000000, -548.000000)">
<g
id="编组-4"
transform="translate(60.000000, 548.000000)">
<rect
id="矩形备份-6"
x="0"
y="0"
width="42"
height="42"
rx="6"></rect>
<g
id="编组"
transform="translate(7.016512, 9.508856)">
<rect
id="矩形"
fill-opacity="0.01"
fill="#FFFFFF"
fill-rule="nonzero"
x="3.48348783"
y="0.99114375"
width="21"
height="21"></rect>
<path
d="M7.69091831,0 C3.44334299,0 0,3.44336396 0,7.69091831 C0,15.3818366 9.08926709,22.3735805 13.9834878,24 C18.8777086,22.3735805 27.9669757,15.3818366 27.9669757,7.69091831 C27.9669757,3.44336396 24.5236117,0 20.2760574,0 C17.6749189,0 15.3753343,1.29130518 13.9834878,3.26780127 C12.5916414,1.29130518 10.2920568,0 7.69091831,0 Z"
id="路径"
stroke="#FFFFFF"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"></path>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,11 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="24px"
height="24px"
viewBox="0 0 24 24"
version="1.1"
xmlns="http://www.w3.org/2000/svg">
<title>编组</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="歌库-播放详情页-车载版-暗色" transform="translate(-44.000000, -719.000000)" fill="#FF5F5F" fill-rule="nonzero">
<g id="编组-5备份" transform="translate(39.000000, 719.000000)">
<g id="编组" transform="translate(5.000000, 0.000000)">
<path d="M12,0 C5.38370036,0 0,5.38287096 0,12 C0,18.617129 5.38287096,24 12,24 C18.6162996,24 24,18.617129 24,12 C24,5.38287096 18.617129,0 12,0 Z M16.8951485,15.6960009 C17.2294354,16.0319926 17.2285599,16.573717 16.8934437,16.9080039 C16.7263233,17.0742949 16.5077352,17.1582928 16.2883176,17.1582928 C16.0680247,17.1582928 15.8486072,17.0742949 15.6814407,16.9062991 L11.9957149,13.2094227 L8.29800911,16.8677328 C8.13088872,17.032319 7.91312997,17.1154415 7.69541729,17.1154415 C7.47429493,17.1154415 7.25400198,17.0305681 7.08600611,16.8608674 C6.75342407,16.5240002 6.75600437,15.9822758 7.09287155,15.6488644 L10.7862922,11.9957148 L7.10655635,8.30399906 C6.77226948,7.96883673 6.77314495,7.42628293 7.10826118,7.09199606 C7.44342351,6.75683373 7.98510185,6.75858466 8.32026418,7.09370089 L12.0051145,10.7897018 L15.7028203,7.13143773 C16.038812,6.79802633 16.5822412,6.80143599 16.9148233,7.13830318 C17.2482347,7.47517034 17.244825,8.01772414 16.9079578,8.35030618 L13.2145833,12.0034097 L16.8951485,15.6960009 Z" id="形状"></path>
<g
id="页面-1"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd">
<g
id="歌库-播放详情页-车载版-暗色"
transform="translate(-44.000000, -719.000000)"
fill="#FF5F5F"
fill-rule="nonzero">
<g
id="编组-5备份"
transform="translate(39.000000, 719.000000)">
<g
id="编组"
transform="translate(5.000000, 0.000000)">
<path
d="M12,0 C5.38370036,0 0,5.38287096 0,12 C0,18.617129 5.38287096,24 12,24 C18.6162996,24 24,18.617129 24,12 C24,5.38287096 18.617129,0 12,0 Z M16.8951485,15.6960009 C17.2294354,16.0319926 17.2285599,16.573717 16.8934437,16.9080039 C16.7263233,17.0742949 16.5077352,17.1582928 16.2883176,17.1582928 C16.0680247,17.1582928 15.8486072,17.0742949 15.6814407,16.9062991 L11.9957149,13.2094227 L8.29800911,16.8677328 C8.13088872,17.032319 7.91312997,17.1154415 7.69541729,17.1154415 C7.47429493,17.1154415 7.25400198,17.0305681 7.08600611,16.8608674 C6.75342407,16.5240002 6.75600437,15.9822758 7.09287155,15.6488644 L10.7862922,11.9957148 L7.10655635,8.30399906 C6.77226948,7.96883673 6.77314495,7.42628293 7.10826118,7.09199606 C7.44342351,6.75683373 7.98510185,6.75858466 8.32026418,7.09370089 L12.0051145,10.7897018 L15.7028203,7.13143773 C16.038812,6.79802633 16.5822412,6.80143599 16.9148233,7.13830318 C17.2482347,7.47517034 17.244825,8.01772414 16.9079578,8.35030618 L13.2145833,12.0034097 L16.8951485,15.6960009 Z"
id="形状"></path>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -1,13 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="42px" height="42px" viewBox="0 0 42 42" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="42px"
height="42px"
viewBox="0 0 42 42"
version="1.1"
xmlns="http://www.w3.org/2000/svg">
<title>编组 4</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="歌库-播放详情页-车载版-暗色" transform="translate(-60.000000, -548.000000)">
<g id="编组-4" transform="translate(60.000000, 548.000000)">
<rect id="矩形备份-6" x="0" y="0" width="42" height="42" rx="6"></rect>
<g id="编组" transform="translate(7.016512, 9.508856)">
<rect id="矩形" fill-opacity="0.01" fill="#FFFFFF" fill-rule="nonzero" x="3.48348783" y="0.99114375" width="21" height="21"></rect>
<path d="M7.69091831,0 C3.44334299,0 0,3.44336396 0,7.69091831 C0,15.3818366 9.08926709,22.3735805 13.9834878,24 C18.8777086,22.3735805 27.9669757,15.3818366 27.9669757,7.69091831 C27.9669757,3.44336396 24.5236117,0 20.2760574,0 C17.6749189,0 15.3753343,1.29130518 13.9834878,3.26780127 C12.5916414,1.29130518 10.2920568,0 7.69091831,0 Z" id="路径" fill="#F940A7"></path>
<g
id="页面-1"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd">
<g
id="歌库-播放详情页-车载版-暗色"
transform="translate(-60.000000, -548.000000)">
<g
id="编组-4"
transform="translate(60.000000, 548.000000)">
<rect
id="矩形备份-6"
x="0"
y="0"
width="42"
height="42"
rx="6"></rect>
<g
id="编组"
transform="translate(7.016512, 9.508856)">
<rect
id="矩形"
fill-opacity="0.01"
fill="#FFFFFF"
fill-rule="nonzero"
x="3.48348783"
y="0.99114375"
width="21"
height="21"></rect>
<path
d="M7.69091831,0 C3.44334299,0 0,3.44336396 0,7.69091831 C0,15.3818366 9.08926709,22.3735805 13.9834878,24 C18.8777086,22.3735805 27.9669757,15.3818366 27.9669757,7.69091831 C27.9669757,3.44336396 24.5236117,0 20.2760574,0 C17.6749189,0 15.3753343,1.29130518 13.9834878,3.26780127 C12.5916414,1.29130518 10.2920568,0 7.69091831,0 Z"
id="路径"
fill="#F940A7"></path>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

@@ -1,13 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<svg width="24px"
height="24px"
viewBox="0 0 24 24"
version="1.1"
xmlns="http://www.w3.org/2000/svg">
<title>编组 2</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="歌库-播放详情页-车载版-暗色" transform="translate(-176.000000, -719.000000)">
<g id="编组-5" transform="translate(162.500000, 719.000000)">
<g id="编组-2" transform="translate(13.500000, 0.000000)">
<rect id="矩形" x="0" y="0" width="24" height="24"></rect>
<g id="编组备份-19" transform="translate(0.000000, 1.000000)" fill="#F940A7">
<path d="M17.4,0 C21.04506,0 24,3.15641696 24,7.05000845 C24,14.1000169 16.2,20.5091155 12,22 C7.8,20.5091155 0,14.1000169 0,7.05000845 C0,3.15641696 2.954922,0 6.6,0 C8.83218,0 10.80558,1.18369642 12,2.9954845 C13.19442,1.18369642 15.16782,0 17.4,0 Z M7.57894737,3.22323712 C5.29268874,4.20700076 3.46368183,6.05741333 2.52631579,8.39970771 L3.8523458,8.39970771 C4.60681115,6.71325575 5.93284115,5.35472501 7.57894737,4.58176786 Z" id="形状结合"></path>
<g
id="页面-1"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd">
<g
id="歌库-播放详情页-车载版-暗色"
transform="translate(-176.000000, -719.000000)">
<g
id="编组-5"
transform="translate(162.500000, 719.000000)">
<g
id="编组-2"
transform="translate(13.500000, 0.000000)">
<rect
id="矩形"
x="0"
y="0"
width="24"
height="24"></rect>
<g
id="编组备份-19"
transform="translate(0.000000, 1.000000)"
fill="#F940A7">
<path
d="M17.4,0 C21.04506,0 24,3.15641696 24,7.05000845 C24,14.1000169 16.2,20.5091155 12,22 C7.8,20.5091155 0,14.1000169 0,7.05000845 C0,3.15641696 2.954922,0 6.6,0 C8.83218,0 10.80558,1.18369642 12,2.9954845 C13.19442,1.18369642 15.16782,0 17.4,0 Z M7.57894737,3.22323712 C5.29268874,4.20700076 3.46368183,6.05741333 2.52631579,8.39970771 L3.8523458,8.39970771 C4.60681115,6.71325575 5.93284115,5.35472501 7.57894737,4.58176786 Z"
id="形状结合"></path>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

@@ -1,5 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path fill="#FFFFFF" fill-rule="evenodd"
d="M16,10.5 C16,9.12 17.12,8.4 18.3,9.05 L37.2,22.05 C38.4,22.72 38.4,24.28 37.2,24.95 L18.3,37.95 C17.12,38.6 16,37.88 16,36.5 Z"/>
<svg width="48px"
height="48px"
viewBox="0 0 48 48"
version="1.1"
xmlns="http://www.w3.org/2000/svg">
<path
fill="#FFFFFF"
fill-rule="evenodd"
d="M16,10.5 C16,9.12 17.12,8.4 18.3,9.05 L37.2,22.05 C38.4,22.72 38.4,24.28 37.2,24.95 L18.3,37.95 C17.12,38.6 16,37.88 16,36.5 Z" />
</svg>

Before

Width:  |  Height:  |  Size: 336 B

After

Width:  |  Height:  |  Size: 367 B

@@ -1,7 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="48px" height="48px" viewBox="0 0 48 48" version="1.1" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<line x1="16" y1="10" x2="16" y2="38" stroke="#FFFFFF" stroke-width="6"/>
<line x1="32" y1="10" x2="32" y2="38" stroke="#FFFFFF" stroke-width="6"/>
<svg width="48px"
height="48px"
viewBox="0 0 48 48"
version="1.1"
xmlns="http://www.w3.org/2000/svg">
<g
fill="none"
fill-rule="evenodd"
stroke-linecap="round"
stroke-linejoin="round">
<line
x1="16"
y1="10"
x2="16"
y2="38"
stroke="#FFFFFF"
stroke-width="6" />
<line
x1="32"
y1="10"
x2="32"
y2="38"
stroke="#FFFFFF"
stroke-width="6" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 602 B

@@ -1 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 9.17071C12.6872 9.06015 12.3506 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15C13.6569 15 15 13.6569 15 12V2.4578C19.0571 3.73207 22 7.52236 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C12.3375 2 12.6711 2.01672 13 2.04938V9.17071Z" fill="rgba(2,252,137,1)"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24">
<path
d="M13 9.17071C12.6872 9.06015 12.3506 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15C13.6569 15 15 13.6569 15 12V2.4578C19.0571 3.73207 22 7.52236 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C12.3375 2 12.6711 2.01672 13 2.04938V9.17071Z"
fill="rgba(2,252,137,1)" />
</svg>

Before

Width:  |  Height:  |  Size: 396 B

After

Width:  |  Height:  |  Size: 417 B

@@ -1,9 +1,12 @@
<svg id="Layer_2" data-name="Layer 2"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
<svg id="Layer_2"
data-name="Layer 2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 1024 1024">
<path
d="M824.4,601.25c-18,20-59.5,127.5-52,183,4.5,8.5,58,12,76-4s49-110,46-134-41-45-49-63-2-37-2-90v-2c0-157.4-127.6-285-285-285s-285,127.6-285,285v2c0,53,6,72-2,90s-46,39-49,63,28,118,46,134,76,4,76,4"
style="fill:none;stroke:#ff0073;stroke-linecap:round;stroke-linejoin:round;stroke-width:10px" />
<path d="M748.07,767.29A285.47,285.47,0,0,1,468.79,825.2"
<path
d="M748.07,767.29A285.47,285.47,0,0,1,468.79,825.2"
style="fill:none;stroke:#f4c42d;stroke-linecap:round;stroke-linejoin:round;stroke-width:10px" />
<path
d="M297.4,626.25c19,71,55,180,155.5,214.5-38.5-43.5-72.5-106.5-79.5-163.5,19,43,46,63,82,76-72-102-81-267-11-383-9,100,20,218,72.5,275.5-19-69-18.5-125.5-4.5-174.5,6,77,28,150,77,186-22-52-27-127-18.5-153.5,8.5,51.5,44.5,98.5,80.5,109.5-28-58-37-228-17.5-247.5,52,0,128.5,43.5,148.5,72.5,10,116-20,199-122,295,39,0,70-11,92-31"

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -1,7 +1,16 @@
<svg height="18" viewBox="0 0 18 18" width="18" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" stroke="#ccc" stroke-linejoin="round" stroke-width="1.4"
<svg height="18"
viewBox="0 0 18 18"
width="18"
xmlns="http://www.w3.org/2000/svg">
<g
fill="none"
fill-rule="evenodd"
stroke="#ccc"
stroke-linejoin="round"
stroke-width="1.4"
transform="translate(1 1)">
<path d="m8 16c-4.41828 0-8-3.58172-8-8s3.58172-8 8-8 8 3.58172 8 8"
<path
d="m8 16c-4.41828 0-8-3.58172-8-8s3.58172-8 8-8 8 3.58172 8 8"
stroke-linecap="round" />
<path d="m6.4 8v-2.77128l2.4 1.38564 2.4 1.38564-2.4 1.38564-2.4 1.38564z" />
<g stroke-linecap="round">

Before

Width:  |  Height:  |  Size: 565 B

After

Width:  |  Height:  |  Size: 629 B

@@ -1,5 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16"
fill="rgba(255,255,255,1)">
<path
d="M8.5 7C9.32843 7 10 6.32843 10 5.5C10 4.67157 9.32843 4 8.5 4C7.67157 4 7 4.67157 7 5.5C7 6.32843 7.67157 7 8.5 7ZM8.5 13.5C9.32843 13.5 10 12.8284 10 12C10 11.1716 9.32843 10.5 8.5 10.5C7.67157 10.5 7 11.1716 7 12C7 12.8284 7.67157 13.5 8.5 13.5ZM10 18.5C10 19.3284 9.32843 20 8.5 20C7.67157 20 7 19.3284 7 18.5C7 17.6716 7.67157 17 8.5 17C9.32843 17 10 17.6716 10 18.5ZM15.5 7C16.3284 7 17 6.32843 17 5.5C17 4.67157 16.3284 4 15.5 4C14.6716 4 14 4.67157 14 5.5C14 6.32843 14.6716 7 15.5 7ZM17 12C17 12.8284 16.3284 13.5 15.5 13.5C14.6716 13.5 14 12.8284 14 12C14 11.1716 14.6716 10.5 15.5 10.5C16.3284 10.5 17 11.1716 17 12ZM15.5 20C16.3284 20 17 19.3284 17 18.5C17 17.6716 16.3284 17 15.5 17C14.6716 17 14 17.6716 14 18.5C14 19.3284 14.6716 20 15.5 20Z" />
<path d="M8.5 7C9.32843 7 10 6.32843 10 5.5C10 4.67157 9.32843 4 8.5 4C7.67157 4 7 4.67157 7 5.5C7 6.32843 7.67157 7 8.5 7ZM8.5 13.5C9.32843 13.5 10 12.8284 10 12C10 11.1716 9.32843 10.5 8.5 10.5C7.67157 10.5 7 11.1716 7 12C7 12.8284 7.67157 13.5 8.5 13.5ZM10 18.5C10 19.3284 9.32843 20 8.5 20C7.67157 20 7 19.3284 7 18.5C7 17.6716 7.67157 17 8.5 17C9.32843 17 10 17.6716 10 18.5ZM15.5 7C16.3284 7 17 6.32843 17 5.5C17 4.67157 16.3284 4 15.5 4C14.6716 4 14 4.67157 14 5.5C14 6.32843 14.6716 7 15.5 7ZM17 12C17 12.8284 16.3284 13.5 15.5 13.5C14.6716 13.5 14 12.8284 14 12C14 11.1716 14.6716 10.5 15.5 10.5C16.3284 10.5 17 11.1716 17 12ZM15.5 20C16.3284 20 17 19.3284 17 18.5C17 17.6716 16.3284 17 15.5 17C14.6716 17 14 17.6716 14 18.5C14 19.3284 14.6716 20 15.5 20Z" />
</svg>

Before

Width:  |  Height:  |  Size: 902 B

After

Width:  |  Height:  |  Size: 906 B

@@ -1,5 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<!-- <path fill="none" d="M0 0h24v24H0z" />-->
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<!-- <path fill="none" d="M0 0h24v24H0z" />-->
<path
d="M6.17 18a3.001 3.001 0 0 1 5.66 0H22v2H11.83a3.001 3.001 0 0 1-5.66 0H2v-2h4.17zm6-7a3.001 3.001 0 0 1 5.66 0H22v2h-4.17a3.001 3.001 0 0 1-5.66 0H2v-2h10.17zm-6-7a3.001 3.001 0 0 1 5.66 0H22v2H11.83a3.001 3.001 0 0 1-5.66 0H2V4h4.17zM9 6a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm6 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2zm-6 7a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"
fill="#fff" />

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 535 B

@@ -1,7 +1,21 @@
<svg height="16" viewBox="0 0 4 16" width="4" xmlns="http://www.w3.org/2000/svg">
<g fill="#ccc" transform="matrix(0 -1 1 0 0 16)">
<circle cx="2" cy="2" r="1.5" />
<circle cx="8" cy="2" r="1.5" />
<circle cx="14" cy="2" r="1.5" />
<svg height="16"
viewBox="0 0 4 16"
width="4"
xmlns="http://www.w3.org/2000/svg">
<g
fill="#ccc"
transform="matrix(0 -1 1 0 0 16)">
<circle
cx="2"
cy="2"
r="1.5" />
<circle
cx="8"
cy="2"
r="1.5" />
<circle
cx="14"
cy="2"
r="1.5" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 411 B

@@ -1,4 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="currentColor">
<path
d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 557 B

@@ -1,5 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="currentColor">
<path d="M19 3L23 8H20V20H18V8H15L19 3ZM14 18V20H3V18H14ZM14 11V13H3V11H14ZM12 4V6H3V4H12Z"
<path
d="M19 3L23 8H20V20H18V8H15L19 3ZM14 18V20H3V18H14ZM14 11V13H3V11H14ZM12 4V6H3V4H12Z"
fill="#fff" />
</svg>

Before

Width:  |  Height:  |  Size: 233 B

After

Width:  |  Height:  |  Size: 253 B

@@ -1,5 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="24"
height="24"
fill="currentColor">
<path d="M20 4V16H23L19 21L15 16H18V4H20ZM12 18V20H3V18H12ZM14 11V13H3V11H14ZM14 4V6H3V4H14Z"
<path
d="M20 4V16H23L19 21L15 16H18V4H20ZM12 18V20H3V18H12ZM14 11V13H3V11H14ZM14 4V6H3V4H14Z"
fill="#fff" />
</svg>

Before

Width:  |  Height:  |  Size: 235 B

After

Width:  |  Height:  |  Size: 255 B

@@ -1,6 +1,15 @@
<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg">
<text fill="#333" fill-rule="evenodd" font-family="PingFangSC-Regular, PingFang SC"
<svg height="32"
viewBox="0 0 32 32"
width="32"
xmlns="http://www.w3.org/2000/svg">
<text
fill="#333"
fill-rule="evenodd"
font-family="PingFangSC-Regular, PingFang SC"
font-size="15">
<tspan x="9" y="22">原</tspan>
<tspan
x="9"
y="22">原
</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 254 B

After

Width:  |  Height:  |  Size: 323 B

@@ -1,8 +1,19 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path d="m0 0h16v16h-16z" fill="#fff" fill-opacity=".01" fill-rule="nonzero" />
<svg height="16"
viewBox="0 0 16 16"
width="16"
xmlns="http://www.w3.org/2000/svg">
<g
fill="none"
fill-rule="evenodd">
<path
d="m0 0h16v16h-16z"
fill="#fff"
fill-opacity=".01"
fill-rule="nonzero" />
<path
d="m5 2.66666667c-2.02504333 0-3.66666667 1.64163333-3.66666667 3.66666666 0 3.66666667 4.33333334 6.99999997 6.66666667 7.77539997 2.3333333-.7754 6.6666667-4.1087333 6.6666667-7.77539997 0-2.02503333-1.6416334-3.66666666-3.6666667-3.66666666-1.2401 0-2.33643333.61563333-3 1.55793333-.66356667-.9423-1.7599-1.55793333-3-1.55793333z"
stroke="#666" stroke-linecap="round" stroke-linejoin="round" />
stroke="#666"
stroke-linecap="round"
stroke-linejoin="round" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 664 B

After

Width:  |  Height:  |  Size: 764 B

@@ -1,4 +1,6 @@
<svg height="14" viewBox="0 0 16 14" width="16" xmlns="http://www.w3.org/2000/svg">
<path
d="m13.7695312 0h.7421876c.6040624 0 1.09375.4896875 1.09375 1.09375v11.5820312c0 .6040625-.4896876 1.09375-1.09375 1.09375h-.7421876c-.6040624 0-1.09375-.4896875-1.09375-1.09375v-11.5820312c0-.6040625.4896876-1.09375 1.09375-1.09375zm-3.2946484 7.54279297-8.24812499 5.92578123c-.63074218.4531446-1.50941406.3091797-1.96257812-.3215625-.17177985-.2391048-.26417969-.5260941-.26417969-.8205078v-10.9190039c0-.77664062.62958984-1.40625 1.40625-1.40625.27772132 0 .54922472.08223197.78027344.23632813l8.25707026 5.50701171c.5025391.33515625.6382227 1.01425782.3030665 1.51679688-.0729441.10937328-.165009.20469996-.2717774.28140625z" />
<svg height="14"
viewBox="0 0 16 14"
width="16"
xmlns="http://www.w3.org/2000/svg">
<path d="m13.7695312 0h.7421876c.6040624 0 1.09375.4896875 1.09375 1.09375v11.5820312c0 .6040625-.4896876 1.09375-1.09375 1.09375h-.7421876c-.6040624 0-1.09375-.4896875-1.09375-1.09375v-11.5820312c0-.6040625.4896876-1.09375 1.09375-1.09375zm-3.2946484 7.54279297-8.24812499 5.92578123c-.63074218.4531446-1.50941406.3091797-1.96257812-.3215625-.17177985-.2391048-.26417969-.5260941-.26417969-.8205078v-10.9190039c0-.77664062.62958984-1.40625 1.40625-1.40625.27772132 0 .54922472.08223197.78027344.23632813l8.25707026 5.50701171c.5025391.33515625.6382227 1.01425782.3030665 1.51679688-.0729441.10937328-.165009.20469996-.2717774.28140625z" />
</svg>

Before

Width:  |  Height:  |  Size: 743 B

After

Width:  |  Height:  |  Size: 747 B

@@ -1,7 +1,20 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path d="m0 0h24v24h-24z" fill="#fff" fill-opacity=".01" fill-rule="nonzero" />
<g stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="3">
<svg height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg">
<g
fill="none"
fill-rule="evenodd">
<path
d="m0 0h24v24h-24z"
fill="#fff"
fill-opacity=".01"
fill-rule="nonzero" />
<g
stroke="#000"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="3">
<path d="m7 5v14" />
<path d="m17 5v14" />
</g>

Before

Width:  |  Height:  |  Size: 397 B

After

Width:  |  Height:  |  Size: 521 B

@@ -1,8 +1,15 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<svg height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg">
<g
fill="none"
fill-rule="evenodd">
<path d="m0 0h24v24h-24z" />
<path
d="m0 2.00005219.00668611-.1638145c.05329261-.6505004.42202581-1.23847486.99325156-1.56827504.61882789-.35728353 1.38127568-.35728353 2.00010357 0 .02380107.01374167.04725059.02793156.0703404.04255552l-.00039784-.00034307.03426759.02227559c.01572831.01041134.0312825.02102712.04665987.03184265l12.01900874 8.01529486c.4976699.36556725.823963.95633395.8299956 1.61988218.0060869.68168952-.3175786 1.28928242-.8266595 1.65138972l-.0733357.0484867-11.99797793 7.9687952c-.32036747.221617-.70171591.3458472-1.10195303.3306011-1.09999419-.0399971-1.99998944-.8999345-1.99998944-1.9998545z"
fill="#000" fill-rule="nonzero" transform="translate(5 2)" />
fill="#000"
fill-rule="nonzero"
transform="translate(5 2)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 861 B

After

Width:  |  Height:  |  Size: 913 B

@@ -1,9 +1,18 @@
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd" stroke="#333" stroke-linejoin="round" stroke-width="1.6"
<svg height="24"
viewBox="0 0 24 24"
width="24"
xmlns="http://www.w3.org/2000/svg">
<g
fill="none"
fill-rule="evenodd"
stroke="#333"
stroke-linejoin="round"
stroke-width="1.6"
transform="translate(3 3)">
<path d="m11.5 0v14.5" stroke-linecap="round" />
<path
d="m4.5 15.02c0-1.39175 1.12825-2.52 2.52-2.52h4.48v2.98c0 1.39175-1.12825 2.52-2.52 2.52h-1.96c-1.39175 0-2.52-1.12825-2.52-2.52z" />
d="m11.5 0v14.5"
stroke-linecap="round" />
<path d="m4.5 15.02c0-1.39175 1.12825-2.52 2.52-2.52h4.48v2.98c0 1.39175-1.12825 2.52-2.52 2.52h-1.96c-1.39175 0-2.52-1.12825-2.52-2.52z" />
<g stroke-linecap="round">
<path d="m11.5 4.0332 6.4417 1.52755v-4.054055l-6.4417-1.506695z" />
<path d="m0 1h7" />

Before

Width:  |  Height:  |  Size: 674 B

After

Width:  |  Height:  |  Size: 738 B

@@ -1,4 +1,7 @@
<svg height="14" viewBox="0 0 16 14" width="16" xmlns="http://www.w3.org/2000/svg">
<svg height="14"
viewBox="0 0 16 14"
width="16"
xmlns="http://www.w3.org/2000/svg">
<path
d="m13.7695312 0h.7421876c.6040624 0 1.09375.4896875 1.09375 1.09375v11.5820312c0 .6040625-.4896876 1.09375-1.09375 1.09375h-.7421876c-.6040624 0-1.09375-.4896875-1.09375-1.09375v-11.5820312c0-.6040625.4896876-1.09375 1.09375-1.09375zm-3.2946484 7.54279297-8.24812499 5.92578123c-.63074218.4531446-1.50941406.3091797-1.96257812-.3215625-.17177985-.2391048-.26417969-.5260941-.26417969-.8205078v-10.9190039c0-.77664062.62958984-1.40625 1.40625-1.40625.27772132 0 .54922472.08223197.78027344.23632813l8.25707026 5.50701171c.5025391.33515625.6382227 1.01425782.3030665 1.51679688-.0729441.10937328-.165009.20469996-.2717774.28140625z"
transform="matrix(-1 0 0 1 16 0)" />

Before

Width:  |  Height:  |  Size: 785 B

After

Width:  |  Height:  |  Size: 797 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M8 20v1.932a.5.5 0 0 1-.82.385l-4.12-3.433A.5.5 0 0 1 3.382 18H18a2 2 0 0 0 2-2V8h2v8a4 4 0 0 1-4 4H8zm8-16V2.068a.5.5 0 0 1 .82-.385l4.12 3.433a.5.5 0 0 1-.321.884H6a2 2 0 0 0-2 2v8H2V8a4 4 0 0 1 4-4h10z"
fill="rgba(153,153,153,1)" />

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 426 B

@@ -1,6 +1,15 @@
<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg">
<text fill="#333" fill-rule="evenodd" font-family="PingFangSC-Regular, PingFang SC"
<svg height="32"
viewBox="0 0 32 32"
width="32"
xmlns="http://www.w3.org/2000/svg">
<text
fill="#333"
fill-rule="evenodd"
font-family="PingFangSC-Regular, PingFang SC"
font-size="15">
<tspan x="9" y="22">音</tspan>
<tspan
x="9"
y="22">音
</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 254 B

After

Width:  |  Height:  |  Size: 323 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M18 17.883V16l5 3-5 3v-2.09a9 9 0 0 1-6.997-5.365L11 14.54l-.003.006A9 9 0 0 1 2.725 20H2v-2h.725a7 7 0 0 0 6.434-4.243L9.912 12l-.753-1.757A7 7 0 0 0 2.725 6H2V4h.725a9 9 0 0 1 8.272 5.455L11 9.46l.003-.006A9 9 0 0 1 18 4.09V2l5 3-5 3V6.117a7 7 0 0 0-5.159 4.126L12.088 12l.753 1.757A7 7 0 0 0 18 17.883z"
fill="rgba(153,153,153,1)" />

Before

Width:  |  Height:  |  Size: 499 B

After

Width:  |  Height:  |  Size: 527 B

@@ -1,5 +1,10 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z" />
<svg xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
width="16"
height="16">
<path
fill="none"
d="M0 0h24v24H0z" />
<path
d="M8 20v1.932a.5.5 0 0 1-.82.385l-4.12-3.433A.5.5 0 0 1 3.382 18H18a2 2 0 0 0 2-2V8h2v8a4 4 0 0 1-4 4H8zm8-17.932a.5.5 0 0 1 .82-.385l4.12 3.433a.5.5 0 0 1-.321.884H6a2 2 0 0 0-2 2v8H2V8a4 4 0 0 1 4-4h10V2.068zM11 8h2v8h-2v-6H9V9l2-1z"
fill="rgba(153,153,153,1)" />

Before

Width:  |  Height:  |  Size: 426 B

After

Width:  |  Height:  |  Size: 454 B

@@ -1,6 +1,15 @@
<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg">
<text fill="#333" fill-rule="evenodd" font-family="PingFangSC-Regular, PingFang SC"
<svg height="32"
viewBox="0 0 32 32"
width="32"
xmlns="http://www.w3.org/2000/svg">
<text
fill="#333"
fill-rule="evenodd"
font-family="PingFangSC-Regular, PingFang SC"
font-size="15">
<tspan x="9" y="22">译</tspan>
<tspan
x="9"
y="22">译
</tspan>
</text>
</svg>

Before

Width:  |  Height:  |  Size: 254 B

After

Width:  |  Height:  |  Size: 323 B

@@ -1,6 +1,10 @@
<svg class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" width="48" height="48">
<svg class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48">
<path
d="M825.002667 187.733333H256.853333c-56.490667 0-102.4 45.909333-102.4 102.4v315.733334c0 56.490667 45.909333 102.4 102.4 102.4h568.149334c56.490667 0 102.4-45.909333 102.4-102.4V290.133333c0-56.490667-45.909333-102.4-102.4-102.4z m34.133333 418.133334c0 18.773333-15.36 34.133333-34.133333 34.133333H256.853333c-18.773333 0-34.133333-15.36-34.133333-34.133333V290.133333c0-18.773333 15.36-34.133333 34.133333-34.133333h568.149334c18.773333 0 34.133333 15.36 34.133333 34.133333v315.733334zM724.48 785.066667h-366.933333c-18.773333 0-34.133333 15.36-34.133334 34.133333s15.36 34.133333 34.133334 34.133333h366.933333c18.773333 0 34.133333-15.36 34.133333-34.133333s-15.36-34.133333-34.133333-34.133333z"
fill="#F940A7"/>
fill="#F940A7" />
</svg>

Before

Width:  |  Height:  |  Size: 873 B

After

Width:  |  Height:  |  Size: 890 B

@@ -1,51 +1,61 @@
<svg class="icon" viewBox="0 0 1024 1024" version="1.1"
xmlns="http://www.w3.org/2000/svg" width="263" height="263">
<path d="M216.234667 864.085333H785.066667" fill="#FFFFFF"/>
<path d="M609.792 709.973333h-218.453333l-27.306667 97.28h273.066667l-27.306667-97.28z"
fill="#D8E3F0"/>
<svg class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="263"
height="263">
<path
d="M216.234667 864.085333H785.066667"
fill="#FFFFFF" />
<path
d="M609.792 709.973333h-218.453333l-27.306667 97.28h273.066667l-27.306667-97.28z"
fill="#D8E3F0" />
<path
d="M853.333333 220.672H341.333333v409.6H113.834667v56.832A22.698667 22.698667 0 0 0 136.533333 709.973333h568.832v-150.528a22.869333 22.869333 0 0 1 22.869334-22.869333H887.466667V254.805333a34.133333 34.133333 0 0 0-34.133334-34.133333zM753.834667 411.306667a57.002667 57.002667 0 1 1 56.832-57.002667 57.173333 57.173333 0 0 1-56.832 57.002667z"
fill="#EDF4FF"/>
fill="#EDF4FF" />
<path
d="M514.730667 630.272h190.634666v-70.826667a22.869333 22.869333 0 0 1 22.869334-22.869333H887.466667V254.805333a34.133333 34.133333 0 0 0-13.653334-27.136H622.592z m238.933333-332.8a57.002667 57.002667 0 1 1-57.002667 56.832 56.832 56.832 0 0 1 57.173334-56.832zM147.968 220.672H341.333333v409.6H113.834667v-375.466667a34.133333 34.133333 0 0 1 34.133333-34.133333z"
fill="#D8E3F0"/>
fill="#D8E3F0" />
<path
d="M705.365333 630.272H113.834667v56.832A22.698667 22.698667 0 0 0 136.533333 709.973333h568.832z"
fill="#96DDFF"/>
<path d="M705.365333 631.637333h-143.189333l-20.650667 76.970667v1.365333h163.84v-78.336z"
fill="#69BAF9"/>
fill="#96DDFF" />
<path
d="M705.365333 631.637333h-143.189333l-20.650667 76.970667v1.365333h163.84v-78.336z"
fill="#69BAF9" />
<path
d="M728.234667 864.085333H273.066667a56.832 56.832 0 0 1 56.832-56.832h341.333333a57.002667 57.002667 0 0 1 57.002667 56.832z"
fill="#EDF4FF"/>
<path d="M705.365333 536.576l227.498667 0 0 364.032-227.498667 0 0-364.032Z" fill="#EDF4FF"
/>
<path d="M780.458667 830.464l77.482666-288.768h66.901334v288.768h-144.384z" fill="#D8E3F0"
/>
fill="#EDF4FF" />
<path
d="M705.365333 536.576l227.498667 0 0 364.032-227.498667 0 0-364.032Z"
fill="#EDF4FF" />
<path
d="M780.458667 830.464l77.482666-288.768h66.901334v288.768h-144.384z"
fill="#D8E3F0" />
<path
d="M705.365333 836.266667h227.669334v40.96a22.698667 22.698667 0 0 1-22.698667 22.698666H728.234667a22.698667 22.698667 0 0 1-22.698667-22.698666V836.266667h-0.170667z"
fill="#96DDFF"/>
fill="#96DDFF" />
<path
d="M802.133333 900.778667h107.52a22.869333 22.869333 0 0 0 22.869334-22.869334V836.266667H819.2z"
fill="#69BAF9"/>
fill="#69BAF9" />
<path
d="M753.834667 354.304m-56.832 0a56.832 56.832 0 1 0 113.664 0 56.832 56.832 0 1 0-113.664 0Z"
fill="#FFC670"/>
fill="#FFC670" />
<path
d="M910.165333 519.509333H904.533333V254.805333a51.2 51.2 0 0 0-51.2-51.2H147.968a51.2 51.2 0 0 0-51.2 51.2v432.298667A39.765333 39.765333 0 0 0 136.533333 727.04h232.277334l-17.066667 63.146667h-21.845333a73.728 73.728 0 0 0-71.68 56.832h-41.984a17.066667 17.066667 0 0 0 0 34.133333h472.405333a39.765333 39.765333 0 0 0 39.594667 36.693333h181.930666a39.936 39.936 0 0 0 39.936-39.936V559.445333a39.936 39.936 0 0 0-39.936-39.936zM358.4 237.738667h494.933333a17.066667 17.066667 0 0 1 17.066667 17.066666v264.704h-142.165333a39.936 39.936 0 0 0-39.936 39.936v53.76H358.4z m329.898667 554.496a79.189333 79.189333 0 0 0-17.066667-2.048h-21.162667l-17.066666-63.146667h55.978666zM130.901333 254.805333a17.066667 17.066667 0 0 1 17.066667-17.066666H324.266667v375.466666H130.901333zM136.533333 692.906667a5.802667 5.802667 0 0 1-5.632-5.802667v-39.765333h557.397334v45.568z m477.866667 97.28H386.56l17.066667-63.146667H597.333333z m-284.672 34.133333h341.333333a38.570667 38.570667 0 0 1 17.066667 3.925333v18.773334H294.058667a39.765333 39.765333 0 0 1 35.84-22.698667z m586.069333 53.589333a5.802667 5.802667 0 0 1-5.802666 5.802667H728.234667a5.802667 5.802667 0 0 1-5.802667-5.802667V853.333333h193.536z m0-58.026666H722.432V559.445333a5.802667 5.802667 0 0 1 5.802667-5.802666h181.930666a5.802667 5.802667 0 0 1 5.802667 5.802666z"
fill="#3D3D63"/>
fill="#3D3D63" />
<path
d="M853.333333 576.512h-68.266666a17.066667 17.066667 0 0 0 0 34.133333h68.266666a17.066667 17.066667 0 0 0 0-34.133333zM284.501333 291.84H238.933333a17.066667 17.066667 0 0 0 0 34.133333h45.568a17.066667 17.066667 0 0 0 0-34.133333z"
fill="#3D3D63"/>
fill="#3D3D63" />
<path
d="M176.298667 308.906667m-17.066667 0a17.066667 17.066667 0 1 0 34.133333 0 17.066667 17.066667 0 1 0-34.133333 0Z"
fill="#3D3D63"/>
fill="#3D3D63" />
<path
d="M284.501333 382.805333H238.933333a17.066667 17.066667 0 0 0 0 34.133334h45.568a17.066667 17.066667 0 0 0 0-34.133334zM284.501333 439.637333H238.933333a17.066667 17.066667 0 0 0 0 34.133334h45.568a17.066667 17.066667 0 0 0 0-34.133334z"
fill="#3D3D63"/>
fill="#3D3D63" />
<path
d="M176.298667 399.872m-17.066667 0a17.066667 17.066667 0 1 0 34.133333 0 17.066667 17.066667 0 1 0-34.133333 0Z"
fill="#3D3D63"/>
fill="#3D3D63" />
<path
d="M753.834667 428.373333a74.069333 74.069333 0 1 0-74.069334-74.069333 74.069333 74.069333 0 0 0 74.069334 74.069333z m0-113.834666a39.936 39.936 0 1 1-39.936 39.765333 39.936 39.936 0 0 1 39.936-39.765333zM423.765333 384h102.4a17.066667 17.066667 0 0 0 0-34.133333h-102.4a17.066667 17.066667 0 0 0 0 34.133333zM575.488 406.698667h-151.722667a17.066667 17.066667 0 0 0 0 34.133333h151.722667a17.066667 17.066667 0 0 0 0-34.133333zM626.688 291.84H423.765333a17.066667 17.066667 0 0 0 0 34.133333h202.922667a17.066667 17.066667 0 0 0 0-34.133333zM517.632 542.037333h-93.866667a17.066667 17.066667 0 0 0 0 34.133334h93.866667a17.066667 17.066667 0 1 0 0-34.133334zM423.765333 519.338667h39.936a17.066667 17.066667 0 0 0 0-34.133334h-39.936a17.066667 17.066667 0 0 0 0 34.133334z"
fill="#3D3D63"/>
fill="#3D3D63" />
</svg>

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

@@ -1,5 +1,9 @@
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
width="48" height="48">
<svg class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48">
<path
d="M647.499 60.657H375.167c-89.602 0-162.497 72.895-162.497 162.497v573.907c0 89.602 72.895 162.497 162.497 162.497h272.332c89.602 0 162.497-72.895 162.497-162.497V223.154c0.001-89.602-72.895-162.497-162.497-162.497z m108.332 736.404c0 59.733-48.598 108.331-108.331 108.331H375.167c-59.733 0-108.331-48.598-108.331-108.331V223.154c0-59.733 48.598-108.332 108.331-108.332h56.168c0.15 14.827 12.193 26.81 27.055 26.81h105.885c14.863 0 26.905-11.983 27.055-26.81h56.168c59.733 0 108.331 48.598 108.331 108.332v573.907z"
fill="#F940A7" />

Before

Width:  |  Height:  |  Size: 900 B

After

Width:  |  Height:  |  Size: 916 B

@@ -1,7 +1,13 @@
<svg class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
width="48" height="48">
<svg class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
width="48"
height="48">
<path
d="M746.672 128H704a32 32 0 1 0 0 64h42.672A85.44 85.44 0 0 1 832 277.328V320a32 32 0 1 0 64 0v-42.672C896 194.992 829.008 128 746.672 128zM320 128h-42.672C194.992 128 128 194.992 128 277.328V320a32 32 0 1 0 64 0v-42.672A85.44 85.44 0 0 1 277.328 192H320a32 32 0 1 0 0-64zM320 832h-42.672A85.44 85.44 0 0 1 192 746.672V704a32 32 0 1 0-64 0v42.672C128 829.008 194.992 896 277.328 896H320a32 32 0 1 0 0-64zM864 672a32 32 0 0 0-32 32v42.672A85.44 85.44 0 0 1 746.672 832H704a32 32 0 1 0 0 64h42.672C829.008 896 896 829.008 896 746.672V704a32 32 0 0 0-32-32z"
fill="#333333" />
<path d="M704 480H320a32 32 0 1 0 0 64h384a32 32 0 1 0 0-64z" fill="#333333" />
<path
d="M704 480H320a32 32 0 1 0 0 64h384a32 32 0 1 0 0-64z"
fill="#333333" />
</svg>

Before

Width:  |  Height:  |  Size: 809 B

After

Width:  |  Height:  |  Size: 841 B

@@ -1,5 +1,9 @@
<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg">
<svg height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg">
<path
d="m12.7550466.4072205v9.6645963c0 1.5403726-1.2670808 2.8074534-2.80745343 2.8074534-1.54037267 0-2.80745342-1.2670808-2.80745342-2.8074534 0-1.5403727 1.26708075-2.80745345 2.80745342-2.80745345.49689443 0 .96894413.12422361 1.39130433.37267081v-7.52795031c-.4720497-.07453416-.9192546-.09937888-1.39130433-.09937888-5.49068323 0-9.9378882 4.44720497-9.9378882 9.9378882s4.44720497 9.93788823 9.9378882 9.93788823 9.93788823-4.447205 9.93788823-9.93788823c0-4.52173913-3.0062112-8.32298137-7.1304348-9.54037267zm-8.49689443 8.09937888h-1.44099378c1.01863354-2.48447205 3.00621118-4.44720497 5.49068322-5.49068323v1.44099379c-1.78881987.81987577-3.22981366 2.26086956-4.04968944 4.04968944zm7.20496893 8.62111802v-1.4409938c1.7142857-.8944099 3.1552795-2.3354037 4.0496895-4.0496894h1.4409937c-1.0434782 2.484472-3.0310559 4.4720497-5.4906832 5.4906832z"
fill="#d1e0f3" fill-rule="evenodd" />
fill="#d1e0f3"
fill-rule="evenodd" />
</svg>

Before

Width:  |  Height:  |  Size: 1010 B

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -1,19 +1,50 @@
<svg height="17" viewBox="0 0 19 17" width="19" xmlns="http://www.w3.org/2000/svg"
<svg height="17"
viewBox="0 0 19 17"
width="19"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="a"
<path
id="a"
d="m13.775 0c2.8856725 0 5.225 2.43904947 5.225 5.4477338 0 5.4477338-6.175 10.4002191-9.5 11.5522662-3.325-1.1520471-9.5-6.1045324-9.5-11.5522662 0-3.00868433 2.33931325-5.4477338 5.225-5.4477338 1.7671425 0 3.3294175.91467451 4.275 2.31469257.9455825-1.40001806 2.5078575-2.31469257 4.275-2.31469257zm-7.775 2.49068323c-1.80995475.760181-3.25791855 2.19004525-4 4h1.04977376c.59728506-1.30316742 1.64705882-2.35294118 2.95022624-2.95022624z" />
<filter id="b" height="135.3%" width="131.6%" x="-15.8%" y="-17.6%">
<feGaussianBlur in="SourceAlpha" result="shadowBlurInner1" stdDeviation="2" />
<feOffset dx="2" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1" />
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" k2="-1" k3="1"
operator="arithmetic" result="shadowInnerInner1" />
<feColorMatrix in="shadowInnerInner1" type="matrix"
<filter
id="b"
height="135.3%"
width="131.6%"
x="-15.8%"
y="-17.6%">
<feGaussianBlur
in="SourceAlpha"
result="shadowBlurInner1"
stdDeviation="2" />
<feOffset
dx="2"
dy="2"
in="shadowBlurInner1"
result="shadowOffsetInner1" />
<feComposite
in="shadowOffsetInner1"
in2="SourceAlpha"
k2="-1"
k3="1"
operator="arithmetic"
result="shadowInnerInner1" />
<feColorMatrix
in="shadowInnerInner1"
type="matrix"
values="0 0 0 0 0.850980392 0 0 0 0 0.121568627 0 0 0 0 0.525490196 0 0 0 1 0" />
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<use fill="#f940a7" fill-rule="evenodd" xlink:href="#a" />
<use fill="#000" filter="url(#b)" xlink:href="#a" />
<g
fill="none"
fill-rule="evenodd">
<use
fill="#f940a7"
fill-rule="evenodd"
xlink:href="#a" />
<use
fill="#000"
filter="url(#b)"
xlink:href="#a" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -1,19 +1,50 @@
<svg height="20" viewBox="0 0 18 20" width="18" xmlns="http://www.w3.org/2000/svg"
<svg height="20"
viewBox="0 0 18 20"
width="18"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="a"
<path
id="a"
d="m17.9377597.59334913c0-.37179364-.3353552-.65176382-.6973235-.58223382l-10.682767 2.05332786c-.55413609.10644212-.95496248.59529924-.95496248 1.16442194v11.40214369c-.85501284-.3450918-1.97410006-.3475587-3.06116675.072271-1.86845245.7216364-2.93433599 2.4172639-2.38071384 3.7872151.55359981 1.3700425 2.5171083 1.8956519 4.38558311 1.1739926 1.57629028-.6087987 2.58135047-1.9107959 2.52472799-3.1256751v-10.42415528l9.39812497-1.8063867v7.96630168c-.8549905-.3471931-1.9753961-.3504595-3.0637141.0701924-1.8669553.7216365-2.9320121 2.417264-2.3788145 3.787238.5531752 1.3700425 2.5150749 1.8956062 4.3821196 1.1739926 1.547197-.5980402 2.5436096-1.8649984 2.5247056-3.0611702l.0042009-.0012792z" />
<filter id="b" height="130%" width="133.5%" x="-16.7%" y="-15%">
<feGaussianBlur in="SourceAlpha" result="shadowBlurInner1" stdDeviation="2" />
<feOffset dx="2" dy="2" in="shadowBlurInner1" result="shadowOffsetInner1" />
<feComposite in="shadowOffsetInner1" in2="SourceAlpha" k2="-1" k3="1"
operator="arithmetic" result="shadowInnerInner1" />
<feColorMatrix in="shadowInnerInner1" type="matrix"
<filter
id="b"
height="130%"
width="133.5%"
x="-16.7%"
y="-15%">
<feGaussianBlur
in="SourceAlpha"
result="shadowBlurInner1"
stdDeviation="2" />
<feOffset
dx="2"
dy="2"
in="shadowBlurInner1"
result="shadowOffsetInner1" />
<feComposite
in="shadowOffsetInner1"
in2="SourceAlpha"
k2="-1"
k3="1"
operator="arithmetic"
result="shadowInnerInner1" />
<feColorMatrix
in="shadowInnerInner1"
type="matrix"
values="0 0 0 0 0.850980392 0 0 0 0 0.121568627 0 0 0 0 0.525490196 0 0 0 1 0" />
</filter>
</defs>
<g fill="none" fill-rule="evenodd">
<use fill="#f940a7" fill-rule="evenodd" xlink:href="#a" />
<use fill="#000" filter="url(#b)" xlink:href="#a" />
<g
fill="none"
fill-rule="evenodd">
<use
fill="#f940a7"
fill-rule="evenodd"
xlink:href="#a" />
<use
fill="#000"
filter="url(#b)"
xlink:href="#a" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -1,5 +1,9 @@
<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg">
<svg height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg">
<path
d="m17.3319825 3.29295049h-14.66396498c-.70898417 0-1.38881399.28217071-1.88939045.78422079-.50057647.50205008-.78072907 1.18268463-.77861519 1.89162697v11.35935375c-.0010499.7082501.27957262 1.3878557.78003363 1.8890397.50046101.5011841 1.17968252.7828083 1.88797201.7828083h14.66396498c.7082895 0 1.387511-.2816242 1.887972-.7828083.500461-.501184.7810835-1.1807896.7800336-1.8890397v-11.35935375c.0021139-.70894234-.2780387-1.38957689-.7786152-1.89162697-.5005764-.50205008-1.1804062-.78422079-1.8893904-.78422079zm-13.90396679 4.25175811c.01725819-.33271549.29909524-.58956564.63199848-.57596723h7.93998101c.3344313-.01586234.6187531.24162259.6359985.57596723v.05599681c-.0255763.3279752-.3074062.57611376-.6359985.55996815h-7.93998101c-.32782914.01605831-.60848136-.23260733-.63199848-.55996815zm6.29998493 7.8275547c-.01717701.2758804-.25180498.4873895-.52799873.4759729h-5.33598725c-.27923844.0159424-.51883501-.196822-.53599871-.4759729v-.2439861c.01926491-.2781231.25753263-.4897076.53599871-.475973h5.33598725c.27846608-.0137346.51673381.1978499.53599872.475973zm-5.77198621-3.5357989c-.31088183.0159446-.57667754-.2212963-.59599855-.5319697v-.1279927c.01932101-.3106734.28511672-.5479144.59599855-.5319698h6.79998377c.3108818-.0159446.5766776.2212964.5959986.5319698v.1279927c-.019321.3106734-.2851168.5479144-.5959986.5319697zm13.29596827-2.1558773c-.2655518.25688267-.6773874.28557456-.9759977.06799612l-.7999981-.55996815v5.12770833c.0162628 1.1938857-.8975383 2.195161-2.087995 2.2878698-1.1486792.0530819-2.1451347-.7858202-2.2885358-1.9266866s.6144493-2.2002518 1.7405371-2.4330654c.3894972-.0822106.794273-.0531016 1.1679972.0839952v-4.53574191c-.0056471-.0451549-.0056471-.09083737 0-.13599227.016262-.09797034.0545579-.19096966.1119998-.27198452v-.0279984c0-.02799842.0439999-.05599681.0679998-.07999546l.0519999-.03999771.0599998-.04799728.0599999-.03199818.0679998-.03599795.0639999-.01999887.0719998-.01999886h.3239992c.0802292.01913082.1561918.05304083.2239995.09999431l1.9999952 1.37192196c.1846353.11875259.3070099.31334993.3340492.53119762s-.0440482.43645497-.1940495.59673822zm-14.58396518-6.8396109h14.66396498c.34846.01821665.6465286-.24771034.6679984-.5959661-.0092805-.16770603-.0849442-.32481902-.2102806-.43664s-.2900336-.16915012-.4577178-.15932608h-14.66396498c-.16768418-.00982405-.33238145.04750509-.45771785.15932607-.12533639.11182099-.20100011.26893397-.21028056.43664001.02146977.34825577.31953841.61418276.66799841.5959661zm1.19599714-1.63990671h12.27197064c.1662308.01093535.3299329-.04504919.4546509-.15548624s.2000959-.26615638.2093475-.43248031v-.02799839c-.0235381-.34389329-.3198958-.60453104-.6639984-.58396679h-12.27197064c-.34410258-.02056425-.64046029.2400735-.66399841.58396679v.02799839c.00925163.16632393.08462954.32204326.20934756.43248031.12471803.11043705.28842011.16642159.45465085.15548624z"
fill="#d1e0f3" fill-rule="evenodd" />
fill="#d1e0f3"
fill-rule="evenodd" />
</svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -1,5 +1,9 @@
<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg">
<svg height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg">
<path
d="m311.000724 10.0000203c-2.65399-.00535426-5.200827 1.04656-7.077485 2.9232183-1.876659 1.8766583-2.928573 4.4234952-2.923219 7.0774853 0 5.5383074 4.514318 9.9992586 10.000704 9.9992586 2.65349.0049719 5.199736-1.0469142 7.07604-2.9232183s2.92819-4.4225502 2.923218-7.0760403c0-5.5383073-4.512872-10.0007036-9.999258-10.0007036zm6.511838 13.0251387c-.124847.2835962-.406969.4652441-.716807.4615816-.103843 0-.206244 0-.308645-.0505354l-5.897432-2.6667527c-.278621-.1313616-.457818-.4102369-.461526-.7182492v-5.5383073c0-.409604.360567-.7687286.770171-.7687286s.768729.3591246.768729.7687286v5.0767817l5.435906 2.4619507c.409604.1543226.563926.5639266.409604.9735306z"
fill="#d1e0f3" transform="translate(-301 -10)" />
fill="#d1e0f3"
transform="translate(-301 -10)" />
</svg>

Before

Width:  |  Height:  |  Size: 837 B

After

Width:  |  Height:  |  Size: 857 B

@@ -1,4 +1,7 @@
<svg height="20" viewBox="0 0 20 20" width="20" xmlns="http://www.w3.org/2000/svg">
<svg height="20"
viewBox="0 0 20 20"
width="20"
xmlns="http://www.w3.org/2000/svg">
<path
d="m12.8 8.20416666c1.0833333-.8375 1.7833333-2.14583332 1.7833333-3.62083332 0-2.52916666-2.0541666-4.58333334-4.5833333-4.58333334-2.52916668 0-4.58333334 2.05416666-4.58333334 4.58333334 0 1.46666666.69583334 2.775 1.775 3.6125-4.19583332.87083332-7.19166666 3.67083336-7.19166666 7.08333336 0 3.8791666 3.05833334 4.6916666 7.65416666 4.7208333h1.72083334v-2.5875c-.75833334-.2083333-1.875-.9083333-1.875-3.2333333 0-3.1833334 2.07083334-3.3458334 2.4875-3.3458334.4208333 0 2.5125.1625 2.5125 3.3333334 0 2.3083333-1.1083333 3.0208333-1.875 3.2375v2.5958333h1.6916667l.5208333-.0083333c3.7208333-.0791667 7.1625-.7041667 7.1625-4.7125 0-3.4041667-2.9958333-6.20000004-7.2-7.07500004z"
fill="#d1e0f3" />

Before

Width:  |  Height:  |  Size: 824 B

After

Width:  |  Height:  |  Size: 836 B

@@ -0,0 +1,286 @@
package com.zhushenwudi.lovelivemusicplayer
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.zIndex
import kotlinx.coroutines.delay
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.compose.runtime.collectAsState
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.zhushenwudi.lovelivemusicplayer.navigation.LlmpTransitionMs
import com.zhushenwudi.lovelivemusicplayer.navigation.Routes
import com.zhushenwudi.lovelivemusicplayer.platform.DetailBackHandler
import com.zhushenwudi.lovelivemusicplayer.navigation.llmpSlideFadeEnter
import com.zhushenwudi.lovelivemusicplayer.navigation.llmpSlideFadeExit
import com.zhushenwudi.lovelivemusicplayer.navigation.llmpSlideFadePopEnter
import com.zhushenwudi.lovelivemusicplayer.navigation.llmpSlideFadePopExit
import com.zhushenwudi.lovelivemusicplayer.theme.ColorMs
import com.zhushenwudi.lovelivemusicplayer.theme.LlmpTheme
import com.zhushenwudi.lovelivemusicplayer.theme.LocalDarkTheme
import com.zhushenwudi.lovelivemusicplayer.ui.MainShell
import com.zhushenwudi.lovelivemusicplayer.ui.icons.InstallCoilSvg
import com.zhushenwudi.lovelivemusicplayer.ui.screens.DailyScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.DataSyncScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.DriveModeScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.LogsScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.MoegirlScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.PermissionsScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.ScanScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.SdScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.SettingsScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.SplashScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.TachieScreen
import com.zhushenwudi.lovelivemusicplayer.ui.screens.TransferScreen
import com.zhushenwudi.lovelivemusicplayer.i18n.SyncI18nWithSystemLocale
import com.zhushenwudi.lovelivemusicplayer.ui.theme.ProvideDesignDensity
@Composable
fun App() {
val settings = AppContainer.settings
var followSystem by remember { mutableStateOf(settings.followSystemTheme) }
var darkPref by remember { mutableStateOf(settings.isDark) }
val systemDark = isSystemInDarkTheme()
val darkTheme = if (followSystem) systemDark else darkPref
// 有 AI 启动图则先进入启动页,否则进入主页
val startDestination = if (settings.aiPicture) Routes.SPLASH else Routes.MAIN
SyncI18nWithSystemLocale()
ObserveDesktopLyricLifecycle()
LlmpTheme(darkTheme = darkTheme) {
InstallCoilSvg()
// 按设计稿 375×812 缩放 UI 密度,使列表/顶栏/迷你播放器尺寸一致。
// 底部栏在 MainShell 中通过 ProvideUnscaledDensity 保持未缩放。
ProvideDesignDensity {
val navController = rememberNavController()
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
val transferState by AppContainer.musicTransfer.state.collectAsState()
val dark = LocalDarkTheme.current
val navBg = if (dark) ColorMs.NightPrimary else ColorMs.LightPrimary
val showMainShell = currentRoute != null && currentRoute != Routes.SPLASH
val overlayOpen = currentRoute != null &&
currentRoute != Routes.MAIN &&
currentRoute != Routes.SPLASH &&
currentRoute != Routes.PLAYER
// 返回动画结束前保持嵌套页在上层,避免主页提前盖住滑出过程。
var shellInFront by remember { mutableStateOf(!overlayOpen) }
LaunchedEffect(overlayOpen) {
if (overlayOpen) {
shellInFront = false
} else {
delay(LlmpTransitionMs.toLong())
shellInFront = true
}
}
Box(
Modifier
.fillMaxSize()
.background(navBg),
) {
// 主页常驻。抽屉嵌套页返回时只滑走上层面,不拆 6 页曲库 + WeSlide。
if (showMainShell) {
Box(
Modifier
.fillMaxSize()
.zIndex(if (shellInFront) 1f else 0f),
) {
MainShell(
isTopDestination = currentRoute == Routes.MAIN,
onOpenSettings = { navController.navigate(Routes.SETTINGS) },
onOpenRoute = { route ->
if (route == Routes.TRANSFER) {
AppContainer.musicTransfer.resetForOpen()
}
navController.navigate(route)
},
)
}
}
NavHost(
navController = navController,
startDestination = startDestination,
modifier = Modifier
.fillMaxSize()
.zIndex(if (shellInFront) 0f else 1f),
enterTransition = { llmpSlideFadeEnter() },
exitTransition = { llmpSlideFadeExit() },
popEnterTransition = { llmpSlideFadePopEnter() },
popExitTransition = { llmpSlideFadePopExit() },
) {
composable(
Routes.SPLASH,
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
popEnterTransition = { EnterTransition.None },
popExitTransition = { ExitTransition.None },
) {
SplashScreen(
onFinished = {
navController.navigate(Routes.MAIN) {
popUpTo(Routes.SPLASH) { inclusive = true }
launchSingleTop = true
}
},
)
}
composable(
Routes.MAIN,
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
popEnterTransition = { EnterTransition.None },
popExitTransition = { ExitTransition.None },
) {
// 返回栈占位。主页在下层绘制,这里不能铺不透明层,否则会挡住点击。
}
composable(
Routes.PLAYER,
enterTransition = { EnterTransition.None },
exitTransition = { ExitTransition.None },
popEnterTransition = { EnterTransition.None },
popExitTransition = { ExitTransition.None },
) {
LaunchedEffect(Unit) {
val popped =
navController.popBackStack(Routes.MAIN, inclusive = false)
if (!popped) {
navController.navigate(Routes.MAIN) {
launchSingleTop = true
}
}
AppContainer.weSlide.show()
}
}
composable(Routes.SETTINGS) {
SettingsScreen(
darkTheme = darkPref,
followSystem = followSystem,
onFollowSystemChange = {
followSystem = it
settings.followSystemTheme = it
},
onDarkThemeChange = {
darkPref = it
settings.isDark = it
followSystem = false
settings.followSystemTheme = false
},
onBack = { navController.popBackStack() },
onOpenSd = { navController.navigate(Routes.SD) },
onOpenLogs = { navController.navigate(Routes.LOGS) },
onOpenPermissions = { navController.navigate(Routes.PERMISSIONS) },
)
}
composable(Routes.SCAN) {
val previous = navController.previousBackStackEntry?.destination?.route
ScanScreen(
onBack = { navController.popBackStack() },
onConnectedHost = { host ->
when (previous) {
Routes.DATA_SYNC -> AppContainer.dataSync.connect(host)
else -> AppContainer.musicTransfer.connect(host)
}
navController.popBackStack()
},
)
}
composable(Routes.TRANSFER) {
TransferScreen(
onBack = { navController.popBackStack() },
onOpenScan = { navController.navigate(Routes.SCAN) },
)
}
composable(Routes.DATA_SYNC) {
DataSyncScreen(
onBack = { navController.popBackStack() },
onOpenScan = { navController.navigate(Routes.SCAN) },
)
}
composable(Routes.DRIVE_MODE) {
DriveModeScreen(onBack = { navController.popBackStack() })
}
composable(Routes.SD) {
SdScreen(onBack = { navController.popBackStack() })
}
composable(Routes.PERMISSIONS) {
PermissionsScreen(onBack = { navController.popBackStack() })
}
composable(Routes.MOEGIRL) {
MoegirlScreen(onBack = { navController.popBackStack() })
}
composable(Routes.TACHIE) {
TachieScreen(onBack = { navController.popBackStack() })
}
composable(Routes.DAILY) {
DailyScreen(onBack = { navController.popBackStack() })
}
composable(Routes.LOGS) {
LogsScreen(onBack = { navController.popBackStack() })
}
}
// 盖过 NavHost 的预测性返回:手势返回改成立即 pop,避免转场卡在中间态。
InterceptNestedNavBack(
navController = navController,
currentRoute = currentRoute,
transferConnected = transferState.connected,
)
}
}
}
}
/**
* 嵌套页(设置/传歌/SD…)用手势返回时,不要走 NavHost 的预测性 pop。
* 预测性转场被取消或中途交接后,返回栈会停在半动画,后面的嵌套页点不开、也滑不回去。
*/
@Composable
private fun InterceptNestedNavBack(
navController: NavHostController,
currentRoute: String?,
transferConnected: Boolean,
) {
val intercept = currentRoute != null &&
currentRoute != Routes.MAIN &&
currentRoute != Routes.SPLASH &&
currentRoute != Routes.PLAYER &&
!(currentRoute == Routes.TRANSFER && transferConnected)
DetailBackHandler(enabled = intercept) {
navController.popBackStack()
}
}
/** 前后台切换时同步桌面歌词显隐。 */
@Composable
private fun ObserveDesktopLyricLifecycle() {
val owner = LocalLifecycleOwner.current
DisposableEffect(owner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_PAUSE -> AppContainer.setAppInBackground(true)
Lifecycle.Event.ON_RESUME -> AppContainer.setAppInBackground(false)
else -> Unit
}
}
owner.lifecycle.addObserver(observer)
if (!owner.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
AppContainer.setAppInBackground(true)
}
onDispose { owner.lifecycle.removeObserver(observer) }
}
}
@@ -0,0 +1,771 @@
package com.zhushenwudi.lovelivemusicplayer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import com.zhushenwudi.lovelivemusicplayer.AppContainer.scope
import com.zhushenwudi.lovelivemusicplayer.data.DataImportService
import com.zhushenwudi.lovelivemusicplayer.db.LibraryRepository
import com.zhushenwudi.lovelivemusicplayer.i18n.I18n
import com.zhushenwudi.lovelivemusicplayer.db.LlmpDatabase
import com.zhushenwudi.lovelivemusicplayer.db.MusicRepository
import com.zhushenwudi.lovelivemusicplayer.domain.GroupKey
import com.zhushenwudi.lovelivemusicplayer.domain.Lyric
import com.zhushenwudi.lovelivemusicplayer.domain.Menu
import com.zhushenwudi.lovelivemusicplayer.domain.Music
import com.zhushenwudi.lovelivemusicplayer.lyric.LoadedLyric
import com.zhushenwudi.lovelivemusicplayer.lyric.LrcParser
import com.zhushenwudi.lovelivemusicplayer.lyric.LyricService
import com.zhushenwudi.lovelivemusicplayer.lyric.PlatformInfo
import com.zhushenwudi.lovelivemusicplayer.network.OssApi
import com.zhushenwudi.lovelivemusicplayer.network.WsClient
import com.zhushenwudi.lovelivemusicplayer.network.createHttpClient
import com.zhushenwudi.lovelivemusicplayer.platform.CarPlayCatalog
import com.zhushenwudi.lovelivemusicplayer.platform.FileSystem
import com.zhushenwudi.lovelivemusicplayer.platform.PlatformServices
import com.zhushenwudi.lovelivemusicplayer.platform.PlatformUi
import com.zhushenwudi.lovelivemusicplayer.platform.createFileSystem
import com.zhushenwudi.lovelivemusicplayer.player.PlayerController
import com.zhushenwudi.lovelivemusicplayer.player.SleepTimer
import com.zhushenwudi.lovelivemusicplayer.settings.AppSettings
import com.zhushenwudi.lovelivemusicplayer.settings.withSortOrder
import com.zhushenwudi.lovelivemusicplayer.splash.SplashPhotoService
import com.zhushenwudi.lovelivemusicplayer.transfer.DataSyncService
import com.zhushenwudi.lovelivemusicplayer.transfer.MusicTransferService
import com.zhushenwudi.lovelivemusicplayer.ui.weslide.WeSlideController
import com.zhushenwudi.lovelivemusicplayer.update.UpdateChecker
import com.zhushenwudi.lovelivemusicplayer.util.PathResolver
import com.zhushenwudi.lovelivemusicplayer.widget.WidgetCoverCache
object AppContainer {
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private var lyricJob: Job? = null
private var desktopLyricJob: Job? = null
private var desktopPlayingJob: Job? = null
private var homeWidgetJob: Job? = null
private var playQueueJob: Job? = null
private var historyJob: Job? = null
private var carPlayNowPlayingJob: Job? = null
private val _playingLyric = MutableStateFlow(LoadedLyric())
private val _historyRevision = MutableStateFlow(0L)
private val _loveRevision = MutableStateFlow(0L)
private val _usbRevision = MutableStateFlow(0L)
private val _libraryRevision = MutableStateFlow(0L)
/** 当前曲目完整歌词,切歌时刷新。 */
val playingLyric: StateFlow<LoadedLyric> = _playingLyric.asStateFlow()
/** 写入播放历史时递增,用于刷新最近播放列表。 */
val historyRevision: StateFlow<Long> = _historyRevision.asStateFlow()
/** 收藏状态变化时递增,用于刷新收藏列表。 */
val loveRevision: StateFlow<Long> = _loveRevision.asStateFlow()
/** USB 插拔后递增,设置/存储页刷新卷列表。 */
val usbRevision: StateFlow<Long> = _usbRevision.asStateFlow()
/** 切存储清库后递增,刷新曲库 UI。 */
val libraryRevision: StateFlow<Long> = _libraryRevision.asStateFlow()
/** 主页 WeSlide 面板控制器。 */
val weSlide = WeSlideController()
val weSlideFooter = WeSlideController(initial = true)
/** 当前团组,MainShell 重组后仍保留。 */
var currentGroup: GroupKey = GroupKey.GROUP_ALL
lateinit var settings: AppSettings
private set
lateinit var database: LlmpDatabase
private set
lateinit var musicRepository: MusicRepository
private set
lateinit var library: LibraryRepository
private set
lateinit var player: PlayerController
private set
lateinit var fileSystem: FileSystem
private set
lateinit var pathResolver: PathResolver
private set
lateinit var ossApi: OssApi
private set
lateinit var dataImport: DataImportService
private set
lateinit var lyricService: LyricService
private set
lateinit var musicTransfer: MusicTransferService
private set
lateinit var dataSync: DataSyncService
private set
lateinit var updateChecker: UpdateChecker
private set
lateinit var sleepTimer: SleepTimer
private set
lateinit var splashPhoto: SplashPhotoService
private set
private lateinit var widgetCoverCache: WidgetCoverCache
private var desktopLyricMusicId: String? = null
private var desktopLyricSnapshot: LoadedLyric? = null
private var latestCurIndex: Int = LYRIC_INDEX_UNSET
private var originLine1Text: String? = null
private var originLine2Text: String? = null
/** 进入后台才允许弹出桌面歌词;前台一律隐藏。 */
private var appInBackground: Boolean = false
val platform get() = PlatformServices
val carPlayCatalog: CarPlayCatalog get() = PlatformServices.carPlayCatalog
val isInitialized: Boolean
get() = ::settings.isInitialized
fun init(
settings: AppSettings,
database: LlmpDatabase,
player: PlayerController,
fileSystem: FileSystem = createFileSystem(),
) {
this.settings = settings
this.database = database
this.musicRepository = MusicRepository(database)
this.library = LibraryRepository(database)
this.player = player
this.fileSystem = fileSystem
this.pathResolver = PathResolver(fileSystem, settings)
// musicPath 保持相对路径;仅播放引擎解析为 file/http URI。
player.resolvePlaybackUri = { pathResolver.playbackUri(it) }
// Android 通知封面用 file:// / https;iOS Now Playing 优先绝对路径。
player.resolveCoverUri = { music ->
pathResolver.absoluteCoverPath(music)
?.takeIf { fileSystem.exists(it) }
?: pathResolver.coverDisplayUri(music)
}
val http = createHttpClient()
this.ossApi = OssApi(http)
this.widgetCoverCache = WidgetCoverCache(
fileSystem = fileSystem,
pathResolver = pathResolver,
ossApi = ossApi,
scope = scope,
onCoverReady = { notifyWidgetCoverReady() },
)
this.dataImport = DataImportService(ossApi, library, settings, pathResolver)
this.lyricService = LyricService(ossApi, library)
bindLyricLoader()
bindDesktopLyric()
bindHomeWidget()
this.musicTransfer = MusicTransferService(
WsClient(http),
ossApi,
library,
fileSystem,
pathResolver,
scope,
onImported = {
_libraryRevision.value++
refreshCarPlayCatalog()
},
)
this.dataSync = DataSyncService(WsClient(http), library, scope)
this.updateChecker = UpdateChecker(ossApi)
this.sleepTimer = SleepTimer(scope) {
player.pause()
}
this.splashPhoto = SplashPhotoService(fileSystem, ossApi, scope)
// 绑定 CarPlay 播放到播放器;从曲库快照刷新。
PlatformServices.carPlayCatalog.onPlay = { musicId, queue ->
val items = queue.mapNotNull { library.getMusicById(it.musicId) }
val idx = items.indexOfFirst { it.musicId == musicId }.coerceAtLeast(0)
if (items.isNotEmpty()) {
player.setQueue(items, idx)
player.play()
persistPlayQueue()
}
}
refreshCarPlayCatalog()
PlatformServices.desktopLyric.pipAutoOpen(settings.enableDesktopLyric)
PlatformServices.carPlay.bind(PlatformServices.carPlayCatalog)
bindCarPlayNowPlaying()
// 启动时恢复播放队列(不自动播放)
restoreSavedPlayQueue(needPlay = false)
bindPlayQueuePersistence()
bindHistoryOnTrackChange()
initSdStorage()
bindUsbMountListener()
}
/**
* 对齐 Flutter `SDUtils.init`:
* Android 空 `SP_SD_PATH` 时写入默认外部 files 目录;iOS 不持久化。
*/
fun initSdStorage() {
if (!isInitialized) return
if (!PlatformInfo.isIos && settings.sdPath.isBlank()) {
settings.sdPath = fileSystem.musicRoot
}
ensureStorageDirs()
}
fun ensureStorageDirs() {
if (!isInitialized) return
val root = pathResolver.storageRoot()
fileSystem.ensureDir(root + "bg/")
fileSystem.ensureDir(root + "splash/")
}
/** 对齐 Flutter `SDUtils.setUsbMountListener`。Android 在平台服务安装后再绑一次。 */
fun bindUsbMountListener() {
if (!isInitialized || PlatformInfo.isIos) return
PlatformServices.usbMount.setOnChanged { onUsbStorageChanged() }
PlatformServices.usbMount.start()
}
private fun onUsbStorageChanged() {
scope.launch {
delay(1_000)
val pathList = fileSystem.listUsbRoots()
if (pathList.isEmpty()) return@launch
val defPath = settings.sdPath
val stillMounted = pathList.any { defPath.contains(it) }
if (!stillMounted) {
clearAllMusicThroughUsb()
settings.sdPath = ""
initSdStorage()
}
_usbRevision.value++
}
}
/**
* 对齐 Flutter `DBLogic.clearAllMusicThroughUsb`:
* 切存储 / 当前卷被拔出时清空曲库与用户列表。
*/
fun clearAllMusicThroughUsb() {
if (!isInitialized) return
settings.dataVersion = ""
library.clearMusic()
library.clearAlbums()
library.clearArtists()
library.clearMenus()
library.clearLove()
library.clearHistory()
clearPlayQueue()
refreshCarPlayCatalog()
_libraryRevision.value++
}
fun storageDisplayName(path: String): String {
val key = path.split("/").getOrNull(2) ?: path.substringAfterLast('/').ifBlank { path }
return if (key == "emulated") I18n.t("internal_storage") else key
}
fun listStorageDevices(): List<SdStorageDevice> {
val paths = fileSystem.listUsbRoots()
val defPath = settings.sdPath
return paths.map { path ->
SdStorageDevice(
name = storageDisplayName(path),
path = path,
chosen = defPath.contains(path),
)
}
}
fun selectStorageDevice(item: SdStorageDevice): Boolean {
if (!fileSystem.exists(item.path)) {
PlatformUi.showToast(I18n.t("storage_not_found"))
_usbRevision.value++
return false
}
clearAllMusicThroughUsb()
settings.sdPath = item.path.trimEnd('/', '\\') + "/"
ensureStorageDirs()
_usbRevision.value++
return true
}
/** 持久化播放队列(仅非空时写入)。 */
fun persistPlayQueue(
queue: List<Music> = player.state.value.queue,
playingId: String? = player.state.value.current?.musicId,
) {
if (!isInitialized) return
if (queue.isEmpty()) return
library.savePlayList(queue, playingId)
}
/** 从播放队列移除指定曲目并更新数据库。 */
fun removeFromPlayQueue(index: Int) {
player.removeAt(index)
val queue = player.state.value.queue
if (queue.isEmpty()) library.clearPlayList() else persistPlayQueue()
}
/** 清空播放队列。 */
fun clearPlayQueue() {
player.clearQueue()
library.clearPlayList()
_playingLyric.value = LoadedLyric()
}
/**
* 从数据库恢复播放队列,默认不自动播放。
*/
fun restoreSavedPlayQueue(needPlay: Boolean = false) {
if (!isInitialized) return
val rows = library.loadPlayListRows()
if (rows.isEmpty()) return
val ids = rows.map { it.musicId }
val playingId = rows.firstOrNull { it.isPlaying != 0L }?.musicId
val musics = library.getMusicByIdsPreservingOrder(ids, settings.enableHttp)
if (musics.isEmpty()) return
val index = playingId
?.let { pid -> musics.indexOfFirst { it.musicId == pid } }
?.takeIf { it >= 0 }
?: 0
player.setQueue(musics, index)
player.setLoopMode(settings.loopMode)
if (needPlay) player.play()
}
/** 队列或当前曲目变化时持久化。 */
private fun bindPlayQueuePersistence() {
playQueueJob?.cancel()
playQueueJob = scope.launch {
player.state
.map { st -> st.queue.map { it.musicId } to st.current?.musicId }
.distinctUntilChanged()
.collect {
persistPlayQueue()
}
}
}
/**
* 写入播放历史并通知最近播放 UI 刷新。
* 切歌或重播同一曲目(id 不变)时调用。
*/
fun recordPlayingHistory(musicId: String?) {
if (!isInitialized || musicId.isNullOrBlank()) return
library.addHistory(musicId)
_historyRevision.value = _historyRevision.value + 1
}
/**
* 切换收藏状态并刷新收藏列表观察者。
* @return 新的收藏状态
*/
fun toggleLove(musicId: String?): Boolean {
if (!isInitialized || musicId.isNullOrBlank()) return false
val loved = library.toggleLove(musicId)
player.syncLoveState(musicId, loved)
_loveRevision.value = _loveRevision.value + 1
refreshCarPlayCatalog()
return loved
}
/**
* 批量设置收藏。`add=true` 加入我喜欢,`false` 取消。
* 对齐 Flutter [PlayerLogic.toggleLoveList]。
*/
fun toggleLoveList(musicList: List<Music>, add: Boolean) {
if (!isInitialized) return
musicList.mapNotNull { it.musicId }.distinct().forEach { id ->
val loved = library.getMusicById(id)?.isLove == true
if (add != loved) {
val nowLoved = library.toggleLove(id)
player.syncLoveState(id, nowLoved)
}
}
_loveRevision.value = _loveRevision.value + 1
refreshCarPlayCatalog()
}
/** 新建手机歌单(id 101…200)。已满返回 false。 */
fun addPhoneMenu(name: String, musicIds: List<String>): Boolean {
if (!isInitialized) return false
val id = library.nextPhoneMenuId()
if (id < 0) return false
library.upsertMenu(
Menu(
id = id,
isPhone = true,
music = musicIds,
date = "",
name = name,
),
)
_libraryRevision.value++
refreshCarPlayCatalog()
return true
}
/** 向已有歌单追加歌曲(去重)。 */
fun insertMusicToMenu(menu: Menu, musicIds: List<String>): Boolean {
if (!isInitialized) return false
library.upsertMenu(menu.copy(music = (menu.music + musicIds).distinct()))
_libraryRevision.value++
refreshCarPlayCatalog()
return true
}
/**
* 每次实际切歌时更新播放历史,使「最近播放」跟随播放顺序。
*/
private fun bindHistoryOnTrackChange() {
historyJob?.cancel()
historyJob = scope.launch {
player.state
.map { it.current?.musicId }
.distinctUntilChanged()
.collect { musicId ->
recordPlayingHistory(musicId)
}
}
}
fun refreshCarPlayCatalog() {
if (!isInitialized) return
val lists = library.findAllListByGroup("all", settings.enableHttp)
val current = player.state.value.current
PlatformServices.carPlayCatalog.setLibrary(
allMusic = lists.music.withSortOrder(settings.sortOrder),
albums = lists.albums.withSortOrder(settings.albumsSortOrder),
love = lists.loved.withSortOrder(settings.loveSortOrder),
recent = lists.recent.withSortOrder(settings.recentSortOrder),
menuList = lists.menus,
musicCover = { pathResolver.coverDisplayUri(it) },
albumCover = { pathResolver.coverDisplayUri(it) },
)
PlatformServices.carPlayCatalog.updateNowPlaying(
music = current,
coverUri = current?.let { pathResolver.coverDisplayUri(it) },
playing = player.state.value.isPlaying,
favorite = current?.isLove == true,
loopMode = player.state.value.loopMode,
)
PlatformServices.carPlay.refreshCatalog()
}
/**
* 切歌时只刷新 CarPlay「正在播放」条目,避免重建根模板打断导航。
*/
private fun bindCarPlayNowPlaying() {
carPlayNowPlayingJob?.cancel()
carPlayNowPlayingJob = scope.launch {
player.state
.map {
listOf(
it.current?.musicId,
it.current?.musicName,
it.current?.isLove,
it.isPlaying,
it.loopMode,
)
}
.distinctUntilChanged()
.collect {
if (!isInitialized) return@collect
val current = player.state.value.current
val cover: (Music) -> String? = { pathResolver.coverDisplayUri(it) }
PlatformServices.carPlayCatalog.updateNowPlaying(
music = current,
coverUri = current?.let(cover),
playing = player.state.value.isPlaying,
favorite = current?.isLove == true,
loopMode = player.state.value.loopMode,
)
val recent = library.findAllListByGroup(
group = "all",
enableHttp = settings.enableHttp,
).recent.withSortOrder(settings.recentSortOrder)
PlatformServices.carPlayCatalog.updateRecent(recent, cover)
PlatformServices.carPlay.refreshNowPlaying()
}
}
}
/** 当前曲目变化时加载歌词(不强制刷新)。 */
private fun bindLyricLoader() {
lyricJob?.cancel()
lyricJob = scope.launch {
player.state
.map { it.current?.musicId }
.distinctUntilChanged()
.collectLatest { musicId ->
if (musicId.isNullOrBlank()) {
_playingLyric.value = LoadedLyric()
return@collectLatest
}
// 先清掉上一首歌词;携带当前 uid,避免另一条 Flow 在网络返回前
// 把旧歌词按新歌曲的进度推给桌面歌词/小组件。
_playingLyric.value = LoadedLyric(raw = Lyric(uid = musicId))
val music = library.getMusicById(musicId)
?: player.state.value.current
?: return@collectLatest
val loaded = runCatching {
lyricService.getLrc(music, forceRefresh = false)
}.getOrDefault(LoadedLyric())
// collectLatest 能取消上一首的加载;再校验一次,防止不可取消的
// 数据源晚返回后覆盖当前歌曲。
if (player.state.value.current?.musicId == musicId) {
_playingLyric.value = loaded
}
}
}
}
/**
* 根据播放位置与已加载歌词同步桌面悬浮歌词。
*/
private fun bindDesktopLyric() {
desktopPlayingJob?.cancel()
desktopPlayingJob = scope.launch {
player.state
.map { it.isPlaying }
.distinctUntilChanged()
.collect {
syncDesktopLyricVisibility()
}
}
desktopLyricJob?.cancel()
desktopLyricJob = scope.launch {
combine(
player.state.map { it.positionMs to it.current?.musicId },
_playingLyric,
) { pair, lyric -> pair to lyric }
.collect { (pair, lyric) ->
val (positionMs, musicId) = pair
val lyricChanged = lyric !== desktopLyricSnapshot
val isFetch = musicId != desktopLyricMusicId || lyricChanged
if (isFetch) {
desktopLyricMusicId = musicId
desktopLyricSnapshot = lyric
latestCurIndex = LYRIC_INDEX_UNSET
originLine1Text = null
originLine2Text = null
}
val currentLyric = lyric.takeIf {
musicId != null && it.raw?.uid == musicId
} ?: LoadedLyric(raw = Lyric(uid = musicId))
pushDesktopLyric(positionMs, currentLyric, isFetch)
}
}
}
/** 切歌 / 播放状态变化时同步主屏小组件(对齐 Flutter HomeWidgetUtil)。 */
private fun bindHomeWidget() {
homeWidgetJob?.cancel()
homeWidgetJob = scope.launch {
player.state
.map { st ->
Triple(st.current?.musicId, st.isPlaying, st.current?.isLove == true)
}
.distinctUntilChanged()
.collect {
pushHomeWidget()
}
}
}
private fun String.replaceLine(): String = replace("\r", "").replace("\n", "")
/**
* 对齐 Flutter [LyricLogic.changePlayingLyric]。
* @param isFetch 切歌 / 重新搜词 / 切换译文时强制刷新。
*/
private fun pushDesktopLyric(positionMs: Long, lyric: LoadedLyric, isFetch: Boolean) {
val musicId = player.state.value.current?.musicId
if (musicId == null) return
if (lyric.main.isEmpty()) {
if (!isFetch && latestCurIndex == LYRIC_INDEX_EMPTY) return
latestCurIndex = LYRIC_INDEX_EMPTY
setNowPlayingLyricLine(null)
postDesktopLyric(I18n.t("no_lyrics"), "", -1)
return
}
val curIndex = LrcParser.currentIndex(lyric.main, positionMs)
if (isFetch) latestCurIndex = LYRIC_INDEX_UNSET
if (curIndex == latestCurIndex) return
if (curIndex < 0) {
latestCurIndex = LYRIC_INDEX_BEFORE_FIRST
setNowPlayingLyricLine(null)
postDesktopLyric(I18n.t("no_lyrics"), "", -1)
return
}
if (curIndex >= lyric.main.size) return
latestCurIndex = curIndex
val mainText = lyric.main[curIndex].text.replaceLine()
setNowPlayingLyricLine(mainText)
val lrcType = lyricService.lrcType
if (lrcType > 0) {
var extText = lyric.translation.getOrNull(curIndex)?.text?.replaceLine()
if (mainText.isEmpty()) extText = ""
postDesktopLyric(mainText, extText, 0)
return
}
val extText = lyric.main.getOrNull(curIndex + 1)?.text?.replaceLine()
if (isFetch) {
postDesktopLyric(mainText, extText)
return
}
if (mainText == originLine1Text) {
postDesktopLyric(null, extText)
return
}
if (mainText == originLine2Text) {
postDesktopLyric(extText, null, 2)
return
}
postDesktopLyric(mainText, extText)
}
private fun postDesktopLyric(
lyricLine1: String?,
lyricLine2: String?,
currentLine: Int = 1,
) {
if (lyricLine1 != null) originLine1Text = lyricLine1
if (lyricLine2 != null) originLine2Text = lyricLine2
PlatformServices.desktopLyric.updateLines(lyricLine1, lyricLine2, currentLine)
}
/** Now Playing 标题只用当前句,不走桌面歌词的换行/高亮动画。 */
private fun setNowPlayingLyricLine(text: String?) {
player.setNowPlayingLyric(text?.takeIf { it.isNotBlank() && it != I18n.t("no_lyrics") })
}
private fun pushHomeWidget(
updateCover: Boolean = true,
) {
val st = player.state.value
val current = st.current ?: return
val coverPath = if (updateCover) widgetCoverCache.resolveForWidget(current) else null
PlatformServices.homeWidget.update(
songName = current.musicName,
artist = current.artist,
isPlaying = st.isPlaying,
favorite = current.isLove == true,
coverPath = coverPath,
playText = I18n.t("play_text"),
)
}
private fun notifyWidgetCoverReady() {
val st = player.state.value
if (st.current == null) return
pushHomeWidget(
updateCover = true,
)
}
/** 小组件封面:优先读本地缓存,HTTP 由 [WidgetCoverCache] 异步落盘。 */
fun widgetCoverSource(music: Music): String? = widgetCoverCache.resolveForWidget(music)
/**
* 对齐 Flutter [SystemLogic.openDesktopLyric]。
* 权限不足时返回 false,调用方应回滚开关。
*/
fun setDesktopLyricEnabled(enable: Boolean): Boolean {
val ok = PlatformServices.desktopLyric.pipAutoOpen(enable)
if (ok) {
settings.enableDesktopLyric = enable
syncDesktopLyricVisibility()
}
return ok
}
/**
* 由 Activity / Scene 生命周期调用。
* 桌面歌词只在「后台 + 设置开启 + 正在播放」同时成立时显示。
*/
fun setAppInBackground(inBackground: Boolean) {
if (!isInitialized) return
appInBackground = inBackground
syncDesktopLyricVisibility()
}
private fun syncDesktopLyricVisibility() {
if (!isInitialized) return
val lyric = PlatformServices.desktopLyric
val playing = player.state.value.isPlaying
lyric.setPlaying(playing)
val shouldShow = appInBackground && settings.enableDesktopLyric && playing
if (shouldShow) lyric.show() else lyric.hide()
}
/** 对齐 Flutter 悬浮窗「译文」按钮:`LyricLogic.toggleTranslate` + `changePlayingLyric(true)`。 */
fun cycleDesktopLyricType() {
lyricService.toggleType()
applyLyricDisplayType()
}
/** 重新加载当前曲目歌词(网络 + 数据库缓存)。 */
suspend fun reloadPlayingLyric(forceRefresh: Boolean = false): LoadedLyric {
val musicId = player.state.value.current?.musicId ?: return LoadedLyric().also {
_playingLyric.value = it
}
val music = library.getMusicById(musicId) ?: player.state.value.current
?: return LoadedLyric().also { _playingLyric.value = it }
val result = runCatching {
lyricService.fetchLrc(music, forceRefresh = forceRefresh)
}.getOrNull()
val loaded = result?.loaded ?: LoadedLyric()
_playingLyric.value = loaded
if (forceRefresh) {
PlatformUi.showToast(
if (result?.ok == true) I18n.t("search_lyric_success") else I18n.t("search_lyric_failed"),
)
}
return loaded
}
/**
* 在应用 `scope` 中异步重新加载歌词。
* 可从立即关闭的对话框安全调用(Compose UI scope 会被取消)。
*/
fun requestReloadPlayingLyric(forceRefresh: Boolean = false) {
scope.launch { reloadPlayingLyric(forceRefresh = forceRefresh) }
}
fun launch(block: suspend () -> Unit) {
scope.launch { block() }
}
/** 仅重新解析已缓存的 fullLrc 并更新 UI。 */
fun applyLyricDisplayType(): LoadedLyric {
latestCurIndex = LYRIC_INDEX_UNSET
val loaded = lyricService.toLoaded()
_playingLyric.value = loaded
return loaded
}
private const val LYRIC_INDEX_UNSET = Int.MIN_VALUE
private const val LYRIC_INDEX_EMPTY = Int.MIN_VALUE + 1
private const val LYRIC_INDEX_BEFORE_FIRST = -1
}
/** 对齐 Flutter `SdCard`:存储卷展示名、应用私有路径、是否选中。 */
data class SdStorageDevice(
val name: String,
val path: String,
val chosen: Boolean,
)
@@ -0,0 +1,21 @@
package com.zhushenwudi.lovelivemusicplayer.i18n
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.text.intl.Locale
@Composable
fun SyncI18nWithSystemLocale() {
val locale = Locale.current
LaunchedEffect(locale.language, locale.region) {
I18n.setLanguageTag("${locale.language}-${locale.region}")
}
}
@Composable
fun tr(key: String, vararg args: Any?): String {
val lang by I18n.lang.collectAsState()
return I18n.t(key, *args).also { lang }
}
@@ -1,4 +1,4 @@
package top.zhushenwudi.llmp.navigation
package com.zhushenwudi.lovelivemusicplayer.navigation
object Routes {
const val SPLASH = "splash"
@@ -26,17 +26,20 @@ object Routes {
fun parseLibraryDetail(route: String): LibraryDetail? = when {
route.startsWith("album/") ->
route.removePrefix("album/").takeIf { it.isNotEmpty() }?.let(LibraryDetail::Album)
route.startsWith("artist/") ->
route.removePrefix("artist/").takeIf { it.isNotEmpty() }?.let(LibraryDetail::Artist)
route.startsWith("menu/") ->
route.removePrefix("menu/").toLongOrNull()?.let(LibraryDetail::Menu)
else -> null
}
}
/**
* Flutter nested Navigator detail destinations — hosted over home (home stays composed)
* so Hero / shared-element pop does not rebuild the library lists.
* 嵌套详情路由 — 叠加在主页之上(主页保持组合)
* 使 Hero 共享元素返回时不重建曲库列表。
*/
sealed class LibraryDetail {
data class Album(val albumId: String) : LibraryDetail()
@@ -44,19 +47,19 @@ sealed class LibraryDetail {
data class Menu(val menuId: Long) : LibraryDetail()
}
enum class TopSection(val title: String) {
LIBRARY("歌库"),
MINE("我的"),
enum class TopSection(val titleKey: String) {
LIBRARY("song_lib"),
MINE("mine"),
}
/** Bottom bar tabs for the current top section (3 icons). */
enum class BottomTab(val title: String) {
SONGS("歌曲"),
ALBUMS("专辑"),
ARTISTS("歌手"),
LOVE("我喜欢"),
MENUS("歌单"),
RECENT("最近播放"),
/** 当前顶部区域对应的底部栏标签(3 个图标)。 */
enum class BottomTab(val titleKey: String) {
SONGS("music"),
ALBUMS("album"),
ARTISTS("singer"),
LOVE("iLove"),
MENUS("songMenu"),
RECENT("history"),
;
companion object {
@@ -65,7 +68,7 @@ enum class BottomTab(val title: String) {
TopSection.MINE -> listOf(LOVE, MENUS, RECENT)
}
/** page = topTabIndex * 3 + bottomIndex */
/** page = topTabIndex * 3 + bottomIndex(页码公式) */
fun fromPage(page: Int): BottomTab = when (page.coerceIn(0, 5)) {
0 -> SONGS
1 -> ALBUMS
@@ -77,14 +80,14 @@ enum class BottomTab(val title: String) {
}
}
/** Full library content pages (6), driven by top*3 + bottom. */
enum class LibraryTab(val title: String) {
SONGS("歌曲"),
ALBUMS("专辑"),
ARTISTS("歌手"),
LOVE("我喜欢"),
MENUS("歌单"),
RECENT("最近播放"),
/** 曲库全部内容页(6 页),由 top*3 + bottom 驱动。 */
enum class LibraryTab(val titleKey: String) {
SONGS("music"),
ALBUMS("album"),
ARTISTS("singer"),
LOVE("iLove"),
MENUS("songMenu"),
RECENT("history"),
;
companion object {
@@ -1,6 +1,6 @@
@file:OptIn(ExperimentalSharedTransitionApi::class)
package top.zhushenwudi.llmp.navigation
package com.zhushenwudi.lovelivemusicplayer.navigation
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.AnimatedVisibility
@@ -28,7 +28,7 @@ import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
import androidx.navigation.NavBackStackEntry
/** Flutter Hero tags: album{id} / singer{uid} / menu{id}. */
/** Hero 共享元素键:album{id} / singer{uid} / menu{id}。 */
object HeroKeys {
fun album(albumId: String) = "album$albumId"
fun singer(uid: String) = "singer$uid"
@@ -38,19 +38,19 @@ object HeroKeys {
val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope?> { null }
val LocalNavAnimatedVisibilityScope = compositionLocalOf<AnimatedVisibilityScope?> { null }
/** Currently open library detail — list items hide their hero source when matched. */
/** 当前打开的曲库详情 — 匹配项隐藏列表侧 Hero 源。 */
val LocalOpenLibraryDetail = compositionLocalOf<LibraryDetail?> { null }
/** Flutter GetX defaultTransitionDuration = 300ms. */
/** 页面转场动画时长 300ms。 */
const val LlmpTransitionMs = 300
val HeroRoundedClip: Shape = RoundedCornerShape(12.dp)
val HeroCircleClip: Shape = CircleShape
/**
* Flutter GetX [RightToLeftFadeTransition]:
* - Incoming: slide from right + fadeIn
* - Outgoing (secondary): slide left only — **no fadeOut** (avoids white flash through both layers)
* 右进左出淡入淡出转场:
* - 进入:从右侧滑入 + 淡入
* - 退出(次级):仅左滑 — 不淡出(避免双层叠加白闪)
*/
fun AnimatedContentTransitionScope<NavBackStackEntry>.llmpSlideFadeEnter(): EnterTransition =
slideInHorizontally(
@@ -77,8 +77,8 @@ fun AnimatedContentTransitionScope<NavBackStackEntry>.llmpSlideFadePopExit(): Ex
) + fadeOut(animationSpec = tween(LlmpTransitionMs, easing = EaseOutQuad))
/**
* Shared-element modifier for a cover already inside an [AnimatedVisibilityScope].
* Place **after** size / aspectRatio; clip is applied via overlay clip (not parent clip).
* 已在 [androidx.compose.animation.AnimatedVisibilityScope] 内的封面共享元素修饰符。
* 放在 size / aspectRatio 之后;裁剪通过 overlay clip 应用。
*/
@Composable
fun Modifier.llmpSharedElement(
@@ -101,7 +101,7 @@ fun Modifier.llmpSharedElement(
}
/**
* Detail-page cover: uses [LocalNavAnimatedVisibilityScope] from the detail overlay.
* 详情页封面:使用详情叠加层的 [com.zhushenwudi.lovelivemusicplayer.navigation.LocalNavAnimatedVisibilityScope]。
*/
@Composable
fun Modifier.llmpDetailSharedElement(
@@ -113,10 +113,10 @@ fun Modifier.llmpDetailSharedElement(
}
/**
* List-side hero source (Google snack pattern): hide when this item's detail is open so
* only one end of the shared element is "visible" at a time — avoids flicker / small-icon flash.
* 列表侧 Hero 源:对应详情打开时隐藏,
* 确保共享元素仅一端可见,避免闪烁/小图标闪现。
*
* [slotModifier] should include size (e.g. fillMaxWidth + aspectRatio, or size(48.dp)).
* `slotModifier` 应包含尺寸(如 fillMaxWidth + aspectRatio 或 size(48.dp))。
*/
@Composable
fun HeroCoverSlot(
@@ -1,7 +1,7 @@
package top.zhushenwudi.llmp.platform
package com.zhushenwudi.lovelivemusicplayer.platform
import androidx.compose.runtime.Composable
/** System back while library detail overlay is open. */
/** 曲库详情叠加层打开时的系统返回。 */
@Composable
expect fun DetailBackHandler(enabled: Boolean, onBack: () -> Unit)
@@ -0,0 +1,190 @@
package com.zhushenwudi.lovelivemusicplayer.share
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import com.zhushenwudi.lovelivemusicplayer.AppContainer
import com.zhushenwudi.lovelivemusicplayer.Const
import com.zhushenwudi.lovelivemusicplayer.domain.Menu
import com.zhushenwudi.lovelivemusicplayer.domain.Music
import com.zhushenwudi.lovelivemusicplayer.i18n.I18n
import com.zhushenwudi.lovelivemusicplayer.platform.PlatformServices
import com.zhushenwudi.lovelivemusicplayer.util.sha512Hex
/**
* 对齐 H5 `jumper.vue`:
* - type=1 + data=SharedMusic JSON → /play(用 neteaseId 拉流和封面)
* - type=2 + data=kvKey → /share
* - 无 type → /open-app
*/
object QqShare {
private val json = Json {
encodeDefaults = true
explicitNulls = true
ignoreUnknownKeys = true
}
fun shareApp() {
AppContainer.launch {
sendToQq(
title = I18n.t("share_app"),
text = "",
url = Const.SHARE_QQ_PAGE,
imageUrl = Const.SHARE_DEFAULT_LOGO,
)
}
}
fun shareMusic(music: Music) {
val id = music.musicId ?: return
val name = music.musicName ?: return
AppContainer.launch {
val payload = SharedMusicPayload(id = id, name = name, neteaseId = music.neteaseId)
sendToQq(
title = I18n.t("outer_share_music"),
text = name,
url = sharePageUrl("1", json.encodeToString(payload)),
imageUrl = resolveCoverUrl(music),
)
}
}
fun shareMenu(menu: Menu) {
if (menu.music.isEmpty()) return
AppContainer.launch {
val musicList = menu.music.mapNotNull { musicId ->
val music = AppContainer.library.getMusicById(musicId) ?: return@mapNotNull null
val id = music.musicId ?: return@mapNotNull null
val name = music.musicName ?: return@mapNotNull null
SharedMusicPayload(id = id, name = name, neteaseId = music.neteaseId)
}
if (musicList.isEmpty()) return@launch
val value = json.encodeToString(ShareMenuPayload(menuName = menu.name, musicList = musicList))
val key = sha512Hex(value)
val resp = runCatching {
AppContainer.ossApi.postJson(
Const.SHARE_KV_URL,
json.encodeToString(ShareKvRequest(key = key, value = value)),
)
}.getOrNull() ?: return@launch
val ok = runCatching {
json.decodeFromString<ShareKvResponse>(resp).success
}.getOrNull() == true
if (!ok) return@launch
val first = menu.music.firstOrNull()?.let { AppContainer.library.getMusicById(it) }
sendToQq(
title = I18n.t("outer_share_menu"),
text = menu.name,
url = sharePageUrl("2", key),
imageUrl = first?.let { resolveCoverUrl(it) } ?: Const.SHARE_DEFAULT_LOGO,
)
}
}
private fun sendToQq(
title: String,
text: String,
url: String,
imageUrl: String,
) {
PlatformServices.share.shareToQq(
title = title,
text = text,
url = url,
imageUrl = imageUrl,
imagePath = null,
)
}
/**
* `https://shareqq.zhushenwudi.top/?type=1&data=...`
* H5 jumper 用正则从 href 取 type/data,再 decodeURIComponent(data)。
*/
private fun sharePageUrl(type: String, data: String): String =
"${Const.SHARE_QQ_PAGE}?type=$type&data=${encodeUriComponent(data)}"
private suspend fun resolveCoverUrl(music: Music): String {
val picUrl = fetchNeteasePicUrl(music.neteaseId)
if (!picUrl.isNullOrBlank()) return toHttps(picUrl)
val httpCover = AppContainer.pathResolver.coverDisplayUri(music)
?.takeIf { it.startsWith("http") }
return httpCover?.let(::toHttps) ?: Const.SHARE_DEFAULT_LOGO
}
private suspend fun fetchNeteasePicUrl(neteaseId: String?): String? {
val id = neteaseId?.trim().orEmpty()
if (id.isEmpty()) return null
return runCatching {
val body = AppContainer.ossApi.fetchText("${Const.BACKEND_URL}?ids=$id")
json.decodeFromString<NeteaseSongDetail>(body).songs.firstOrNull()?.al?.picUrl
?.takeIf { it.isNotBlank() }
}.getOrNull()
}
private fun toHttps(url: String): String =
if (url.startsWith("http://")) "https://${url.removePrefix("http://")}" else url
/**
* 对齐 JS `encodeURIComponent`:只放过 ASCII 的字母数字和 `-_.!~*'()`。
* 不能对 UTF-8 字节做 `isLetterOrDigit()`,否则 `E5`/`E3` 会被当成 `å`/`ã` 原样写入,
* 日文歌名会变成 `å%83%95ã%82%89...`。
*/
private fun encodeUriComponent(value: String): String {
val bytes = value.encodeToByteArray()
return buildString(bytes.size * 3) {
for (b in bytes) {
val ub = b.toInt() and 0xFF
if (isUriComponentUnreserved(ub)) {
append(ub.toChar())
} else {
append('%')
append(ub.toString(16).uppercase().padStart(2, '0'))
}
}
}
}
private fun isUriComponentUnreserved(ub: Int): Boolean {
val c = ub.toChar()
return ub < 0x80 && (c.isLetterOrDigit() || c in "-_.!~*'()")
}
}
@Serializable
private data class SharedMusicPayload(
@SerialName("_id") val id: String,
val name: String,
val neteaseId: String? = null,
)
@Serializable
private data class ShareMenuPayload(
val menuName: String,
val musicList: List<SharedMusicPayload>,
)
@Serializable
private data class ShareKvRequest(
val key: String,
val value: String,
)
@Serializable
private data class ShareKvResponse(
val success: Boolean = false,
)
@Serializable
private data class NeteaseSongDetail(
val songs: List<NeteaseSong> = emptyList(),
)
@Serializable
private data class NeteaseSong(
val al: NeteaseAlbumInfo? = null,
)
@Serializable
private data class NeteaseAlbumInfo(
val picUrl: String? = null,
)
@@ -1,8 +1,8 @@
package top.zhushenwudi.llmp.theme
package com.zhushenwudi.lovelivemusicplayer.theme
import androidx.compose.ui.graphics.Color
/** Flutter ColorMs 精确色板 */
/** 应用精确色板 */
object ColorMs {
val LightPrimary = Color(0xFFF2F8FF)
val NightPrimary = Color(0xFF303030)
@@ -37,12 +37,12 @@ object ColorMs {
val Dark2B333A = Color(0xFF2B333A)
val E7F2FF = Color(0xFFE7F2FF)
// Flutter leftovers
// 扩展色
val B2246A = Color(0xFFB2246A)
val EBF3FE = Color(0xFFEBF3FE)
val BFBFBF = Color(0xFFBFBFBF)
// Daily / drive / sync
// 每日推荐 / 驾驶模式 / 同步
val DailyPageBg = Color(0xFFF7D7EA)
val DailyItemText = Color(0xFF370521)
val DailyRecentHeader = Color(0xB5B5E2FC)
@@ -1,4 +1,4 @@
package top.zhushenwudi.llmp.theme
package com.zhushenwudi.lovelivemusicplayer.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -16,8 +16,8 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.jetbrains.compose.resources.Font
import top.zhushenwudi.llmp.generated.resources.Res
import top.zhushenwudi.llmp.generated.resources.katong
import com.zhushenwudi.lovelivemusicplayer.generated.resources.Res
import com.zhushenwudi.lovelivemusicplayer.generated.resources.katong
val LocalDarkTheme = staticCompositionLocalOf { false }
@@ -58,7 +58,7 @@ fun LlmpTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
// KaTong is only for「歌库/我的」tabs (TopLibraryTabs); keep Material defaults elsewhere.
// KaTong 字体仅用于「歌库/我的」标签(TopLibraryTabs);其余保持 Material 默认。
val base = Typography()
val typography = base.copy(
titleMedium = base.titleMedium.copy(fontWeight = FontWeight.Medium),
@@ -1,11 +1,11 @@
package top.zhushenwudi.llmp.ui
package com.zhushenwudi.lovelivemusicplayer.ui
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.grid.LazyGridState
/**
* Flutter [HomeController] — survives NavHost leave when opening detail routes,
* so tab index + list scroll restore on back.
* 主页 UI 状态 — 打开详情路由离开 NavHost 后仍保留,
* 返回时恢复标签页索引与列表滚动位置。
*/
object HomeUiState {
var pageIndex: Int = 0
@@ -1,12 +1,10 @@
package top.zhushenwudi.llmp.ui.components
package com.zhushenwudi.lovelivemusicplayer.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
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.material3.Text
import androidx.compose.runtime.Composable
@@ -17,14 +15,14 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import top.zhushenwudi.llmp.domain.Album
import top.zhushenwudi.llmp.navigation.HeroCoverSlot
import top.zhushenwudi.llmp.navigation.HeroKeys
import top.zhushenwudi.llmp.navigation.HeroRoundedClip
import top.zhushenwudi.llmp.navigation.LocalOpenLibraryDetail
import top.zhushenwudi.llmp.navigation.hidesAlbum
import top.zhushenwudi.llmp.theme.ColorMs
import top.zhushenwudi.llmp.theme.LocalDarkTheme
import com.zhushenwudi.lovelivemusicplayer.domain.Album
import com.zhushenwudi.lovelivemusicplayer.navigation.HeroCoverSlot
import com.zhushenwudi.lovelivemusicplayer.navigation.HeroKeys
import com.zhushenwudi.lovelivemusicplayer.navigation.HeroRoundedClip
import com.zhushenwudi.lovelivemusicplayer.navigation.LocalOpenLibraryDetail
import com.zhushenwudi.lovelivemusicplayer.navigation.hidesAlbum
import com.zhushenwudi.lovelivemusicplayer.theme.ColorMs
import com.zhushenwudi.lovelivemusicplayer.theme.LocalDarkTheme
@Composable
fun AlbumGridItem(
@@ -39,7 +37,8 @@ fun AlbumGridItem(
modifier = modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(horizontal = 5.dp, vertical = 4.dp),
// Material 3 双行网格单元内边距。
.padding(horizontal = 5.dp, vertical = AlbumGridItemVerticalPadding),
horizontalAlignment = Alignment.CenterHorizontally,
) {
HeroCoverSlot(
@@ -56,7 +55,6 @@ fun AlbumGridItem(
modifier = Modifier.fillMaxSize().then(heroMod),
)
}
Spacer(Modifier.height(6.dp))
Text(
text = album.albumName.orEmpty(),
color = if (dark) ColorMs.White else ColorMs.Text333,
@@ -65,6 +63,7 @@ fun AlbumGridItem(
maxLines = 2,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
lineHeight = 16.sp,
)
}
}
@@ -0,0 +1,360 @@
package com.zhushenwudi.lovelivemusicplayer.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil3.compose.AsyncImage
import org.jetbrains.compose.resources.ExperimentalResourceApi
import com.zhushenwudi.lovelivemusicplayer.domain.GroupKey
import com.zhushenwudi.lovelivemusicplayer.generated.resources.Res
import com.zhushenwudi.lovelivemusicplayer.i18n.tr
import com.zhushenwudi.lovelivemusicplayer.navigation.Routes
import com.zhushenwudi.lovelivemusicplayer.theme.ColorMs
import com.zhushenwudi.lovelivemusicplayer.theme.LocalDarkTheme
import com.zhushenwudi.lovelivemusicplayer.theme.neuShadowColor
import com.zhushenwudi.lovelivemusicplayer.ui.icons.SvgAsset
import com.zhushenwudi.lovelivemusicplayer.ui.theme.hSp
data class DrawerMenuItem(
val titleKey: String,
val route: String?,
val action: DrawerAction = DrawerAction.Navigate,
val icon: SvgAsset,
)
enum class DrawerAction {
Navigate,
UpdateData,
CheckUpdate,
Share,
}
/** 与 Flutter `DrawerLayout` 对齐的抽屉尺寸。 */
private object DrawerLayout {
val logoSlotHeight = 130.dp
val defaultLogoSize = 165.dp
val groupLogoSize = 130.dp
val logoToTitleGap = 5.dp
val titleFontSize = 17.3.sp
val headerToGroupsGap = 10.dp
val groupRowGap = 12.dp
val groupsToMenuGap = 10.dp
val groupButtonWidth = 120.dp
val groupButtonHeight = 50.dp
val groupButtonGap = 10.dp
val menuOuterPaddingH = 8.dp
val menuInnerPaddingH = 10.dp
val menuInnerPaddingV = 12.dp
val menuItemGap = 8.dp
val menuToVersionGap = 6.dp
val versionHeight = 24.dp
val versionFontSize = 12.sp
}
private data class GroupLogo(
val key: GroupKey,
val asset: SvgAsset,
)
@OptIn(ExperimentalResourceApi::class)
@Composable
fun AppDrawer(
onClose: () -> Unit,
onOpenRoute: (String) -> Unit,
onOpenSettings: () -> Unit,
onUpdateData: () -> Unit = {},
onCheckUpdate: () -> Unit = {},
onShare: () -> Unit = {},
currentGroup: GroupKey = GroupKey.GROUP_ALL,
onGroupSelect: (GroupKey) -> Unit = {},
version: String = "0.2.0",
modifier: Modifier = Modifier,
) {
val dark = LocalDarkTheme.current
val bg = if (dark) ColorMs.NightPrimary else ColorMs.White
val groups = listOf(
GroupLogo(GroupKey.GROUP_ALL, SvgAsset("drawable/drawer_logo_lovelive.svg")),
GroupLogo(GroupKey.GROUP_US, SvgAsset("drawable/drawer_logo_us.svg")),
GroupLogo(GroupKey.GROUP_AQOURS, SvgAsset("drawable/drawer_logo_aqours.svg")),
GroupLogo(GroupKey.GROUP_NIJIGASAKI, SvgAsset("drawable/drawer_logo_nijigasaki.svg")),
GroupLogo(GroupKey.GROUP_LIELLA, SvgAsset("drawable/drawer_logo_liella.svg")),
GroupLogo(GroupKey.GROUP_HASUNOSORA, SvgAsset("drawable/drawer_logo_hasunosora.svg")),
GroupLogo(
GroupKey.GROUP_YOHANE,
SvgAsset(
if (dark) "drawable/drawer_logo_yohane_night.svg"
else "drawable/drawer_logo_yohane_day.svg",
),
),
GroupLogo(GroupKey.GROUP_MUSICAL, SvgAsset("drawable/drawer_logo_musical.svg")),
GroupLogo(GroupKey.GROUP_BLUEBIRD, SvgAsset("drawable/drawer_logo_bluebird.svg")),
GroupLogo(GroupKey.GROUP_COMBINE, SvgAsset("drawable/drawer_logo_allstars.svg")),
)
val groupRows = groups.chunked(2)
val menus = listOf(
DrawerMenuItem(
"daily_news",
Routes.DAILY,
icon = SvgAsset("drawable/drawer_drawer_calendar.svg")
),
DrawerMenuItem(
"music_trans",
Routes.TRANSFER,
icon = SvgAsset("drawable/drawer_drawer_quick_trans.svg")
),
DrawerMenuItem(
"data_sync",
Routes.DATA_SYNC,
icon = SvgAsset("drawable/drawer_drawer_data_sync.svg")
),
DrawerMenuItem(
"fetch_songs",
null,
DrawerAction.UpdateData,
SvgAsset("drawable/drawer_drawer_data_download.svg")
),
DrawerMenuItem(
"drive_mode",
Routes.DRIVE_MODE,
icon = SvgAsset("drawable/drawer_drawer_car.svg")
),
DrawerMenuItem(
"update",
null,
DrawerAction.CheckUpdate,
SvgAsset("drawable/drawer_drawer_update.svg")
),
DrawerMenuItem(
"system_settings",
Routes.SETTINGS,
DrawerAction.Navigate,
SvgAsset("drawable/drawer_drawer_setting.svg")
),
DrawerMenuItem(
"share",
null,
DrawerAction.Share,
SvgAsset("drawable/drawer_drawer_share.svg")
),
)
// 根据 currentGroup 获取团组 Logo
val topLogoSize = if (currentGroup.isDefaultGroupLogo()) {
DrawerLayout.defaultLogoSize
} else {
DrawerLayout.groupLogoSize
}
Column(
modifier = modifier
.fillMaxSize()
.background(bg)
.statusBarsPadding()
.navigationBarsPadding(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Box(
Modifier
.height(DrawerLayout.logoSlotHeight)
.fillMaxWidth(),
contentAlignment = Alignment.Center,
) {
GroupLogoImage(
group = currentGroup,
modifier = Modifier.size(topLogoSize),
)
}
Spacer(Modifier.height(DrawerLayout.logoToTitleGap))
Text(
text = "LoveLiveMusicPlayer",
color = if (dark) ColorMs.EDF5FF else ColorMs.Text333,
fontSize = DrawerLayout.titleFontSize,
fontWeight = FontWeight.Bold,
)
Spacer(Modifier.height(DrawerLayout.headerToGroupsGap))
Column(
Modifier
.weight(1f)
.fillMaxWidth()
.padding(horizontal = DrawerLayout.menuOuterPaddingH)
.shadow(
elevation = 6.dp,
shape = RoundedCornerShape(8.dp),
ambientColor = neuShadowColor(dark),
spotColor = neuShadowColor(dark),
)
.clip(RoundedCornerShape(8.dp))
.background(bg)
.padding(
horizontal = DrawerLayout.menuInnerPaddingH,
vertical = DrawerLayout.menuInnerPaddingV,
)
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
) {
groupRows.forEachIndexed { index, row ->
Row(
horizontalArrangement = Arrangement.spacedBy(DrawerLayout.groupButtonGap),
verticalAlignment = Alignment.CenterVertically,
) {
row.forEach { group ->
key(group.asset.path) {
GroupButton(
group = group,
selected = currentGroup == group.key,
onClick = { onGroupSelect(group.key) },
modifier = Modifier
.width(DrawerLayout.groupButtonWidth)
.height(DrawerLayout.groupButtonHeight),
)
}
}
}
if (index < groupRows.lastIndex) {
Spacer(Modifier.height(DrawerLayout.groupRowGap))
}
}
Spacer(Modifier.height(DrawerLayout.groupsToMenuGap))
Column(
Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(DrawerLayout.menuItemGap),
) {
menus.forEach { item ->
DrawerFunctionRow(
item = item,
onClick = {
when (item.action) {
// 原项目数据更新 / 版本升级不关抽屉,弹窗叠在抽屉上。
DrawerAction.UpdateData -> onUpdateData()
DrawerAction.CheckUpdate -> onCheckUpdate()
DrawerAction.Share -> {
onClose()
onShare()
}
DrawerAction.Navigate -> {
onClose()
when (item.route) {
Routes.SETTINGS -> onOpenSettings()
null -> Unit
else -> onOpenRoute(item.route)
}
}
}
},
)
}
}
}
Spacer(Modifier.height(DrawerLayout.menuToVersionGap))
Box(
Modifier
.fillMaxWidth()
.height(DrawerLayout.versionHeight),
contentAlignment = Alignment.Center,
) {
Text(
"Ver.$version",
color = if (dark) ColorMs.White else ColorMs.Text333,
fontSize = DrawerLayout.versionFontSize,
)
}
}
}
@OptIn(ExperimentalResourceApi::class)
@Composable
private fun GroupButton(
group: GroupLogo,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
NeumorphicSurface(
onClick = onClick,
modifier = modifier,
radius = 8.dp,
background = if (selected) {
Color.Black.copy(alpha = 0.06f)
} else {
Color.Transparent
},
hasShadow = true,
contentPadding = 0.dp,
) {
key(group.asset.path) {
AsyncImage(
model = Res.getUri(group.asset.path),
contentDescription = group.key.displayName,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxSize(),
)
}
}
}
@Composable
private fun DrawerFunctionRow(
item: DrawerMenuItem,
onClick: () -> Unit,
) {
val dark = LocalDarkTheme.current
val interaction = remember { MutableInteractionSource() }
val iconTint = if (dark) ColorMs.White else ColorMs.Text333
Row(
Modifier
.fillMaxWidth()
.height(30.dp)
.clickable(
interactionSource = interaction,
indication = null,
onClick = onClick,
),
verticalAlignment = Alignment.CenterVertically,
) {
NeumorphicIconButton(
icon = item.icon,
onClick = null,
size = 28.dp,
iconSize = 15.dp,
radius = 6.dp,
iconColor = iconTint,
hasShadow = true,
contentPadding = 5.dp,
interactionSource = interaction,
)
Spacer(Modifier.width(8.dp))
Text(
text = tr(item.titleKey),
color = if (dark) ColorMs.EDF5FF else ColorMs.Text333,
fontSize = hSp(15f),
)
}
}
@@ -0,0 +1,82 @@
package com.zhushenwudi.lovelivemusicplayer.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.zhushenwudi.lovelivemusicplayer.domain.Artist
import com.zhushenwudi.lovelivemusicplayer.i18n.tr
import com.zhushenwudi.lovelivemusicplayer.navigation.HeroCircleClip
import com.zhushenwudi.lovelivemusicplayer.navigation.HeroCoverSlot
import com.zhushenwudi.lovelivemusicplayer.navigation.HeroKeys
import com.zhushenwudi.lovelivemusicplayer.navigation.LocalOpenLibraryDetail
import com.zhushenwudi.lovelivemusicplayer.navigation.hidesArtist
import com.zhushenwudi.lovelivemusicplayer.theme.ColorMs
import com.zhushenwudi.lovelivemusicplayer.theme.LocalDarkTheme
@Composable
fun ArtistListItem(
artist: Artist,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val dark = LocalDarkTheme.current
val openDetail = LocalOpenLibraryDetail.current
Row(
modifier = modifier
.fillMaxWidth()
.height(TwoLineListItemHeight)
.clickable(onClick = onClick)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
HeroCoverSlot(
key = HeroKeys.singer(artist.uid),
visible = !openDetail.hidesArtist(artist.uid),
clipShape = HeroCircleClip,
slotModifier = Modifier.size(ListLeadingCoverSize),
) { heroMod ->
ArtistPhotoImage(
artist = artist,
size = ListLeadingCoverSize,
modifier = Modifier.fillMaxSize().then(heroMod),
)
}
Spacer(Modifier.width(10.dp))
TwoLineListTextColumn(
modifier = Modifier.weight(1f),
title = {
Text(
text = artist.name,
color = if (dark) ColorMs.White else ColorMs.Text333,
fontSize = ListItemTitleSize,
fontWeight = FontWeight.Medium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
lineHeight = ListItemTitleLineHeight,
)
},
subtitle = {
Text(
text = tr("count_songs", artist.music.size),
color = ColorMs.Gray999,
fontSize = ListItemSubtitleSize,
lineHeight = ListItemSubtitleLineHeight,
)
},
)
Spacer(Modifier.width(10.dp))
}
}
@@ -0,0 +1,157 @@
package com.zhushenwudi.lovelivemusicplayer.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.zhushenwudi.lovelivemusicplayer.i18n.tr
import com.zhushenwudi.lovelivemusicplayer.theme.ColorMs
import com.zhushenwudi.lovelivemusicplayer.theme.LocalDarkTheme
import com.zhushenwudi.lovelivemusicplayer.ui.icons.SvgAsset
/**
* 批量操作底栏:高度 102,顶部圆角 16,等宽列,
* 新拟态 36 图标,着色 D1E0F3 / #666,标题 15sp。
*/
@Composable
fun BatchActionBar(
showUnlove: Boolean,
onAddToPlaylist: () -> Unit,
onAddToMenu: () -> Unit,
onUnlove: () -> Unit,
modifier: Modifier = Modifier,
/** 歌单详情:删除选中曲目。 */
showRemoveFromMenu: Boolean = false,
onRemoveFromMenu: () -> Unit = {},
) {
val dark = LocalDarkTheme.current
val bg = if (dark) ColorMs.NightPrimary else ColorMs.LightPrimary
val iconTint = if (dark) ColorMs.UnselectedIcon else ColorMs.Gray666
val textColor = if (dark) ColorMs.UnselectedIcon else ColorMs.Text333
val shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
Column(
modifier = modifier
.fillMaxWidth()
.shadow(
elevation = 16.dp,
shape = shape,
ambientColor = bg,
spotColor = bg,
)
.clip(shape)
.background(bg)
.navigationBarsPadding(),
) {
// 底栏内容区高度 102.h(系统导航栏上方)
Row(
Modifier
.fillMaxWidth()
.height(102.dp),
verticalAlignment = Alignment.CenterVertically,
) {
BatchActionItem(
icon = SvgAsset("drawable/dialog_ic_add_play_list2.svg"),
title = tr("add_to_playlist_short"),
iconTint = iconTint,
textColor = textColor,
onClick = onAddToPlaylist,
modifier = Modifier.weight(1f),
)
BatchActionItem(
icon = SvgAsset("drawable/dialog_ic_add_play_list.svg"),
title = tr("add_to_menu"),
iconTint = iconTint,
textColor = textColor,
onClick = onAddToMenu,
modifier = Modifier.weight(1f),
)
if (showUnlove) {
BatchActionItem(
icon = SvgAsset("drawable/dialog_ic_delete_2.svg"),
title = tr("cancel_i_like"),
iconTint = iconTint,
textColor = textColor,
onClick = onUnlove,
modifier = Modifier.weight(1f),
)
}
if (showRemoveFromMenu) {
BatchActionItem(
icon = SvgAsset("drawable/dialog_ic_delete_2.svg"),
title = tr("delete_from_menu"),
iconTint = iconTint,
textColor = textColor,
onClick = onRemoveFromMenu,
modifier = Modifier.weight(1f),
)
}
}
}
}
@Composable
private fun BatchActionItem(
icon: SvgAsset,
title: String,
iconTint: androidx.compose.ui.graphics.Color,
textColor: androidx.compose.ui.graphics.Color,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val interaction = remember { MutableInteractionSource() }
Column(
modifier = modifier
.fillMaxHeight()
.clickable(
interactionSource = interaction,
indication = null,
onClick = onClick,
),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
// 新拟态按钮 36×36,无阴影
NeumorphicIconButton(
icon = icon,
onClick = null,
size = 36.dp,
iconSize = 20.dp,
radius = 6.dp,
iconColor = iconTint,
hasShadow = false,
contentPadding = 4.dp,
interactionSource = interaction,
)
Text(
text = title,
color = textColor,
fontSize = 15.sp,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
modifier = Modifier.padding(horizontal = 4.dp),
)
Spacer(Modifier.height(20.dp))
}
}
@@ -1,16 +1,17 @@
package top.zhushenwudi.llmp.ui.components
package com.zhushenwudi.lovelivemusicplayer.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.navigationBarsPadding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.Text
@@ -24,17 +25,21 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.flow.distinctUntilChanged
import top.zhushenwudi.llmp.navigation.BottomTab
import top.zhushenwudi.llmp.navigation.TopSection
import top.zhushenwudi.llmp.theme.ColorMs
import top.zhushenwudi.llmp.theme.LocalDarkTheme
import top.zhushenwudi.llmp.ui.icons.LlmpIcons
import top.zhushenwudi.llmp.ui.icons.SvgAsset
import top.zhushenwudi.llmp.ui.icons.TintIcon
import com.zhushenwudi.lovelivemusicplayer.i18n.tr
import com.zhushenwudi.lovelivemusicplayer.navigation.BottomTab
import com.zhushenwudi.lovelivemusicplayer.navigation.TopSection
import com.zhushenwudi.lovelivemusicplayer.theme.ColorMs
import com.zhushenwudi.lovelivemusicplayer.theme.LocalDarkTheme
import com.zhushenwudi.lovelivemusicplayer.ui.icons.LlmpIcons
import com.zhushenwudi.lovelivemusicplayer.ui.icons.SvgAsset
import com.zhushenwudi.lovelivemusicplayer.ui.icons.TintIcon
import com.zhushenwudi.lovelivemusicplayer.ui.theme.HomeListChrome
/**
* Flutter WeSlide footer: TabBarView([BottomBar1, BottomBar2]).
* Horizontal swipe switches 歌库 / 我的 and syncs with top tabs.
* WeSlide 底部栏:歌库/我的双页切换。
*
* 外层 [com.zhushenwudi.lovelivemusicplayer.ui.MainShell] 的 footer 槽位高度已含导航栏安全区,此处不再叠加 inset,
* 仅在完整槽位内垂直居中标签行。
*/
@Composable
fun LlmpBottomBar(
@@ -45,6 +50,7 @@ fun LlmpBottomBar(
modifier: Modifier = Modifier,
) {
val dark = LocalDarkTheme.current
val tabStripHeight = HomeListChrome.BottomBarContentHeight
val pagerState = rememberPagerState(
initialPage = if (section == TopSection.LIBRARY) 0 else 1,
pageCount = { 2 },
@@ -52,7 +58,6 @@ fun LlmpBottomBar(
val latestSection = rememberUpdatedState(section)
val latestOnSectionChange = rememberUpdatedState(onSectionChange)
// Top / content driven section → move footer pager (cancels prior anim on rapid taps)
LaunchedEffect(section) {
val target = if (section == TopSection.LIBRARY) 0 else 1
if (pagerState.settledPage != target) {
@@ -60,8 +65,6 @@ fun LlmpBottomBar(
}
}
// Footer swipe → section. Ignore in-progress settles so a cancelled anim toward
// 「我的」cannot overwrite a newer 「歌库」selection (and vice versa).
LaunchedEffect(pagerState) {
snapshotFlow { pagerState.settledPage }
.distinctUntilChanged()
@@ -74,17 +77,17 @@ fun LlmpBottomBar(
}
}
Column(
Box(
modifier = modifier
.fillMaxWidth()
.background(if (dark) ColorMs.BottomBarDark else ColorMs.BottomBarLight)
.navigationBarsPadding(),
.fillMaxSize()
.background(if (dark) ColorMs.BottomBarDark else ColorMs.BottomBarLight),
contentAlignment = Alignment.Center,
) {
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.height(56.dp),
.height(tabStripHeight),
beyondViewportPageCount = 1,
) { page ->
val pageSection = if (page == 0) TopSection.LIBRARY else TopSection.MINE
@@ -98,14 +101,15 @@ fun LlmpBottomBar(
val selected = pageSection == section && index == selectedIndex
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.weight(1f)
.fillMaxHeight()
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() },
) {
if (pageSection != section) {
// Section state drives footer pager via LaunchedEffect(section)
onSectionChange(pageSection)
}
onSelect(index)
@@ -115,11 +119,11 @@ fun LlmpBottomBar(
asset = tab.icon,
tint = if (selected) ColorMs.PinkF940A7 else ColorMs.UnselectedIcon,
size = 18.dp,
contentDescription = tab.title,
contentDescription = tr(tab.titleKey),
)
Spacer(Modifier.height(5.dp))
Spacer(Modifier.height(4.dp))
Text(
text = tab.title,
text = tr(tab.titleKey),
fontSize = 10.sp,
color = if (selected) {
ColorMs.BottomSelectedText

Some files were not shown because too many files have changed in this diff Show More