Ver código fonte

[优化] 增加控件

HapeLee 7 meses atrás
pai
commit
dd88417f8a
81 arquivos alterados com 2449 adições e 1696 exclusões
  1. 0 3
      app/src/main/java/io/legado/app/ui/about/AboutScreen.kt
  2. 4 2
      app/src/main/java/io/legado/app/ui/about/AppLogSheet.kt
  3. 18 20
      app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt
  4. 89 90
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt
  5. 4 13
      app/src/main/java/io/legado/app/ui/book/group/GroupEditSheet.kt
  6. 36 37
      app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt
  7. 54 56
      app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt
  8. 1 1
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt
  9. 25 26
      app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt
  10. 82 91
      app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt
  11. 21 22
      app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt
  12. 11 10
      app/src/main/java/io/legado/app/ui/config/coverConfig/CoverConfigScreen.kt
  13. 13 12
      app/src/main/java/io/legado/app/ui/config/coverConfig/CoverManageSheet.kt
  14. 4 2
      app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt
  15. 5 3
      app/src/main/java/io/legado/app/ui/config/otherConfig/CheckSourceBottomSheet.kt
  16. 4 2
      app/src/main/java/io/legado/app/ui/config/otherConfig/DirectLinkUploadBottomSheet.kt
  17. 22 25
      app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfigScreen.kt
  18. 3 2
      app/src/main/java/io/legado/app/ui/config/readConfig/PageKeySheet.kt
  19. 4 3
      app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt
  20. 13 12
      app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt
  21. 4 2
      app/src/main/java/io/legado/app/ui/config/themeConfig/LauncherIconPickerSheet.kt
  22. 2 2
      app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt
  23. 61 39
      app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt
  24. 4 2
      app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeListDialog.kt
  25. 3 2
      app/src/main/java/io/legado/app/ui/dict/rule/DictRuleEditSheet.kt
  26. 81 90
      app/src/main/java/io/legado/app/ui/dict/rule/DictRuleScreen.kt
  27. 3 2
      app/src/main/java/io/legado/app/ui/main/MainScreen.kt
  28. 11 12
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt
  29. 54 57
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt
  30. 39 58
      app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageSheet.kt
  31. 9 13
      app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt
  32. 1 1
      app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt
  33. 9 13
      app/src/main/java/io/legado/app/ui/main/rss/RssScreen.kt
  34. 64 74
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt
  35. 10 17
      app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt
  36. 44 56
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceScreen.kt
  37. 3 2
      app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt
  38. 225 32
      app/src/main/java/io/legado/app/ui/theme/AppTheme.kt
  39. 98 11
      app/src/main/java/io/legado/app/ui/theme/LegadoTheme.kt
  40. 3 2
      app/src/main/java/io/legado/app/ui/theme/ThemeManager.kt
  41. 8 0
      app/src/main/java/io/legado/app/ui/theme/ThemeResolver.kt
  42. 14 14
      app/src/main/java/io/legado/app/ui/widget/CollapsibleHeader.kt
  43. 0 49
      app/src/main/java/io/legado/app/ui/widget/components/AppIcon.kt
  44. 2 2
      app/src/main/java/io/legado/app/ui/widget/components/AppNavigationBar.kt
  45. 127 0
      app/src/main/java/io/legado/app/ui/widget/components/AppTextField.kt
  46. 1 0
      app/src/main/java/io/legado/app/ui/widget/components/EmptyMessageView.kt
  47. 13 26
      app/src/main/java/io/legado/app/ui/widget/components/GroupManageBottomSheet.kt
  48. 30 100
      app/src/main/java/io/legado/app/ui/widget/components/SearchBarSection.kt
  49. 52 0
      app/src/main/java/io/legado/app/ui/widget/components/ValueStepper.kt
  50. 16 25
      app/src/main/java/io/legado/app/ui/widget/components/bookmark/BookmarkEditSheet.kt
  51. 4 5
      app/src/main/java/io/legado/app/ui/widget/components/bookmark/BookmarkItem.kt
  52. 74 0
      app/src/main/java/io/legado/app/ui/widget/components/button/AppButton.kt
  53. 61 0
      app/src/main/java/io/legado/app/ui/widget/components/button/AppIconButton.kt
  54. 4 4
      app/src/main/java/io/legado/app/ui/widget/components/button/MiuixButton.kt
  55. 101 35
      app/src/main/java/io/legado/app/ui/widget/components/button/SmallIconButton.kt
  56. 33 16
      app/src/main/java/io/legado/app/ui/widget/components/button/SmallTextButton.kt
  57. 4 3
      app/src/main/java/io/legado/app/ui/widget/components/button/TopBarButton.kt
  58. 0 1
      app/src/main/java/io/legado/app/ui/widget/components/card/SelectionItemCard.kt
  59. 4 17
      app/src/main/java/io/legado/app/ui/widget/components/card/TextCard.kt
  60. 0 16
      app/src/main/java/io/legado/app/ui/widget/components/cover/BookshelfCover.kt
  61. 3 2
      app/src/main/java/io/legado/app/ui/widget/components/dialog/ColorPickerSheet.kt
  62. 24 12
      app/src/main/java/io/legado/app/ui/widget/components/divider/PillDivider.kt
  63. 2 3
      app/src/main/java/io/legado/app/ui/widget/components/divider/PillHeaderDivider.kt
  64. 11 16
      app/src/main/java/io/legado/app/ui/widget/components/filePicker/FilePickerSheet.kt
  65. 88 0
      app/src/main/java/io/legado/app/ui/widget/components/icon/AppIcon.kt
  66. 54 0
      app/src/main/java/io/legado/app/ui/widget/components/icon/AppIcons.kt
  67. 72 107
      app/src/main/java/io/legado/app/ui/widget/components/importComponents/ImportComponents.kt
  68. 2 1
      app/src/main/java/io/legado/app/ui/widget/components/lazylist/VerticalFastScroller.kt
  69. 42 18
      app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenu.kt
  70. 89 24
      app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenuItem.kt
  71. 138 0
      app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/AppModalBottomSheet.kt
  72. 0 64
      app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/GlassModalBottomSheet.kt
  73. 4 2
      app/src/main/java/io/legado/app/ui/widget/components/rules/RuleEditSheet.kt
  74. 166 109
      app/src/main/java/io/legado/app/ui/widget/components/settingItem/CompactSettingItems.kt
  75. 1 2
      app/src/main/java/io/legado/app/ui/widget/components/settingItem/ListSettingItem.kt
  76. 4 15
      app/src/main/java/io/legado/app/ui/widget/components/settingItem/SwitchSettingItem.kt
  77. 3 3
      app/src/main/java/io/legado/app/ui/widget/components/tabRow/AppTabRow.kt
  78. 1 2
      app/src/main/java/io/legado/app/ui/widget/components/text/AnimatedText.kt
  79. 52 77
      app/src/main/java/io/legado/app/ui/widget/components/text/AppText.kt
  80. 3 6
      app/src/main/java/io/legado/app/ui/widget/components/topbar/DynamicTopAppBar.kt
  81. 1 1
      app/src/main/java/io/legado/app/ui/widget/components/topbar/GlassMediumFlexibleTopAppBar.kt

+ 0 - 3
app/src/main/java/io/legado/app/ui/about/AboutScreen.kt

@@ -15,7 +15,6 @@ import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.FilledTonalIconButton
 import androidx.compose.material3.Icon
-import androidx.compose.material3.MaterialTheme.colorScheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -91,8 +90,6 @@ fun AboutScreen(
                     .wrapContentWidth(Alignment.CenterHorizontally))
             TextCard(
                 text = versionName,
-                backgroundColor = colorScheme.tertiaryContainer,
-                contentColor = colorScheme.onTertiaryContainer,
                 cornerRadius = 8.dp,
                 modifier = Modifier
                     .fillMaxWidth()

+ 4 - 2
app/src/main/java/io/legado/app/ui/about/AppLogSheet.kt

@@ -31,7 +31,7 @@ import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.constant.AppLog
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.LogUtils
 import java.util.Date
@@ -39,12 +39,14 @@ import java.util.Date
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun AppLogSheet(
+    show: Boolean,
     onDismissRequest: () -> Unit
 ) {
     var logs by remember { mutableStateOf(AppLog.logs) }
     var showStackTrace by remember { mutableStateOf<String?>(null) }
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismissRequest
     ) {
         Row(

+ 18 - 20
app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt

@@ -48,7 +48,6 @@ import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
 import io.legado.app.ui.widget.components.lazylist.Scroller
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
-import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
 import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
 import org.koin.androidx.compose.koinViewModel
@@ -128,7 +127,7 @@ fun AllBookmarkScreen(
                             onDismissRequest = { showMenu = false }
                         ) {
                             RoundDropdownMenuItem(
-                                text = { AppText("导出 JSON") },
+                                text = "导出 JSON",
                                 onClick = {
                                     showMenu = false
                                     pendingExportIsMd = false
@@ -136,7 +135,7 @@ fun AllBookmarkScreen(
                                 }
                             )
                             RoundDropdownMenuItem(
-                                text = { AppText("导出 Markdown") },
+                                text = "导出 Markdown",
                                 onClick = {
                                     showMenu = false
                                     pendingExportIsMd = true
@@ -238,22 +237,21 @@ fun AllBookmarkScreen(
             }
         }
 
-        if (showBottomSheet && editingBookmark != null) {
-            BookmarkEditSheet(
-                bookmark = editingBookmark!!,
-                onDismiss = {
-                    showBottomSheet = false
-                    editingBookmark = null
-                },
-                onSave = { updatedBookmark ->
-                    viewModel.updateBookmark(updatedBookmark)
-                    showBottomSheet = false
-                },
-                onDelete = { bookmarkToDelete ->
-                    viewModel.deleteBookmark(bookmarkToDelete)
-                    showBottomSheet = false
-                }
-            )
-        }
+        BookmarkEditSheet(
+            show = showBottomSheet && editingBookmark != null,
+            bookmark = editingBookmark ?: Bookmark(),
+            onDismiss = {
+                showBottomSheet = false
+                editingBookmark = null
+            },
+            onSave = { updatedBookmark ->
+                viewModel.updateBookmark(updatedBookmark)
+                showBottomSheet = false
+            },
+            onDelete = { bookmarkToDelete ->
+                viewModel.deleteBookmark(bookmarkToDelete)
+                showBottomSheet = false
+            }
+        )
     }
 }

+ 89 - 90
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt

@@ -90,7 +90,7 @@ import io.legado.app.ui.widget.components.card.TextCard
 import io.legado.app.ui.widget.components.cover.Cover
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
 import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
@@ -180,112 +180,111 @@ fun ExploreShowScreen(
         }
     }
 
-    if (showGridCountSheet) {
 
-        GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = showGridCountSheet,
+        modifier = Modifier
+            .padding(16.dp),
+        onDismissRequest = { showGridCountSheet = false }
+    ) {
+        Row(
             modifier = Modifier
-                .padding(16.dp),
-            onDismissRequest = { showGridCountSheet = false }
+                .fillMaxWidth()
+                .padding(bottom = 16.dp),
+            verticalAlignment = Alignment.CenterVertically,
+            horizontalArrangement = Arrangement.Center
         ) {
-            Row(
-                modifier = Modifier
-                    .fillMaxWidth()
-                    .padding(bottom = 16.dp),
-                verticalAlignment = Alignment.CenterVertically,
-                horizontalArrangement = Arrangement.Center
-            ) {
-                AppText(
-                    text = "布局列数",
-                    style = LegadoTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
-                )
+            AppText(
+                text = "布局列数",
+                style = LegadoTheme.typography.titleMedium.copy(fontWeight = FontWeight.Bold)
+            )
 
-                Spacer(modifier = Modifier.width(12.dp))
+            Spacer(modifier = Modifier.width(12.dp))
 
-                TextCard(
-                    text = "$gridColumnCount 列",
-                    textStyle = LegadoTheme.typography.titleSmall,
-                    verticalPadding = 4.dp,
-                    horizontalPadding = 12.dp,
-                    cornerRadius = 12.dp
-                )
-            }
-
-            Slider(
-                value = gridColumnCount.toFloat(),
-                onValueChange = {
-                    val col = it.toInt().coerceIn(1, 10)
-                    viewModel.saveGridCount(col)
-                },
-                valueRange = 1f..10f,
-                steps = 8,
-                modifier = Modifier.padding(horizontal = 20.dp)
+            TextCard(
+                text = "$gridColumnCount 列",
+                textStyle = LegadoTheme.typography.titleSmall,
+                verticalPadding = 4.dp,
+                horizontalPadding = 12.dp,
+                cornerRadius = 12.dp
             )
+        }
+
+        Slider(
+            value = gridColumnCount.toFloat(),
+            onValueChange = {
+                val col = it.toInt().coerceIn(1, 10)
+                viewModel.saveGridCount(col)
+            },
+            valueRange = 1f..10f,
+            steps = 8,
+            modifier = Modifier.padding(horizontal = 20.dp)
+        )
 
-            Spacer(Modifier.height(24.dp))
+        Spacer(Modifier.height(24.dp))
 
-            OutlinedButton(
-                onClick = { showGridCountSheet = false },
-                modifier = Modifier
-                    .fillMaxWidth()
-                    .padding(horizontal = 16.dp)
-            ) {
-                AppText("完成")
-            }
+        OutlinedButton(
+            onClick = { showGridCountSheet = false },
+            modifier = Modifier
+                .fillMaxWidth()
+                .padding(horizontal = 16.dp)
+        ) {
+            AppText("完成")
         }
     }
 
-    if (showKindSheet) {
-        GlassModalBottomSheet(
-            onDismissRequest = { showKindSheet = false }
-        ) {
 
-            var kindQuery by remember { mutableStateOf("") }
+    AppModalBottomSheet(
+        show = showKindSheet,
+        onDismissRequest = { showKindSheet = false }
+    ) {
 
-            SearchBarSection(
-                query = kindQuery,
-                backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh,
-                onQueryChange = { kindQuery = it },
-                placeholder = "选择或搜索分类",
-            )
+        var kindQuery by remember { mutableStateOf("") }
 
-            val filteredKinds = remember(kindQuery, kinds) {
-                if (kindQuery.isBlank()) kinds
-                else kinds.filter { kind ->
-                    kind.title.contains(kindQuery, ignoreCase = true) ||
-                            (kind.url?.contains(kindQuery, ignoreCase = true) == true)
-                }
+        SearchBarSection(
+            query = kindQuery,
+            backgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+            onQueryChange = { kindQuery = it },
+            placeholder = "选择或搜索分类",
+        )
+
+        val filteredKinds = remember(kindQuery, kinds) {
+            if (kindQuery.isBlank()) kinds
+            else kinds.filter { kind ->
+                kind.title.contains(kindQuery, ignoreCase = true) ||
+                        (kind.url?.contains(kindQuery, ignoreCase = true) == true)
             }
+        }
 
-            LazyVerticalGrid(
-                columns = GridCells.Fixed(3),
-                contentPadding = PaddingValues(16.dp),
-                horizontalArrangement = Arrangement.spacedBy(8.dp),
-                verticalArrangement = Arrangement.spacedBy(8.dp),
-                modifier = Modifier.weight(1f, fill = false)
-            ) {
-                itemsIndexed(
-                    items = filteredKinds,
-                    key = { index, kind -> "${kind.url ?: kind.title}_$index" },
-                    span = { _, kind ->
-                        val isClickable = !kind.url.isNullOrBlank()
-                        if (isClickable) GridItemSpan(1) else GridItemSpan(3)
-                    }
-                ) { _, kind ->
-                    KindGridItem(
-                        modifier = Modifier.animateItem(),
-                        kind = kind,
-                        currentTitle = selectedTitle ?: title,
-                        onClick = {
-                            showKindSheet = false
-                            viewModel.switchExploreUrl(kind)
-                        }
-                    )
+        LazyVerticalGrid(
+            columns = GridCells.Fixed(3),
+            contentPadding = PaddingValues(16.dp),
+            horizontalArrangement = Arrangement.spacedBy(8.dp),
+            verticalArrangement = Arrangement.spacedBy(8.dp),
+            modifier = Modifier.weight(1f, fill = false)
+        ) {
+            itemsIndexed(
+                items = filteredKinds,
+                key = { index, kind -> "${kind.url ?: kind.title}_$index" },
+                span = { _, kind ->
+                    val isClickable = !kind.url.isNullOrBlank()
+                    if (isClickable) GridItemSpan(1) else GridItemSpan(3)
                 }
+            ) { _, kind ->
+                KindGridItem(
+                    modifier = Modifier.animateItem(),
+                    kind = kind,
+                    currentTitle = selectedTitle ?: title,
+                    onClick = {
+                        showKindSheet = false
+                        viewModel.switchExploreUrl(kind)
+                    }
+                )
             }
-
         }
     }
 
+
     AppScaffold(
         modifier = Modifier
             .nestedScroll(scrollBehavior.nestedScrollConnection),
@@ -339,7 +338,7 @@ fun ExploreShowScreen(
                         onDismissRequest = { showMenu = false }
                     ) {
                         RoundDropdownMenuItem(
-                            text = { AppText("全部显示") },
+                            text = "全部显示",
                             onClick = {
                                 viewModel.setFilterState(BookFilterState.SHOW_ALL)
                                 showMenu = false
@@ -351,7 +350,7 @@ fun ExploreShowScreen(
                         )
 
                         RoundDropdownMenuItem(
-                            text = { AppText("隐藏已在书架的同源书籍") },
+                            text = "隐藏已在书架的同源书籍",
                             onClick = {
                                 viewModel.setFilterState(BookFilterState.HIDE_IN_SHELF)
                                 showMenu = false
@@ -363,7 +362,7 @@ fun ExploreShowScreen(
                         )
 
                         RoundDropdownMenuItem(
-                            text = { AppText("隐藏已在书架的非同源书籍") },
+                            text = "隐藏已在书架的非同源书籍",
                             onClick = {
                                 viewModel.setFilterState(BookFilterState.HIDE_SAME_NAME_AUTHOR)
                                 showMenu = false
@@ -375,7 +374,7 @@ fun ExploreShowScreen(
                         )
 
                         RoundDropdownMenuItem(
-                            text = { AppText("只显示不在书架的书籍") },
+                            text = "只显示不在书架的书籍",
                             onClick = {
                                 viewModel.setFilterState(BookFilterState.SHOW_NOT_IN_SHELF_ONLY)
                                 showMenu = false

+ 4 - 13
app/src/main/java/io/legado/app/ui/book/group/GroupEditSheet.kt

@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
@@ -36,9 +35,8 @@ import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.data.entities.BookGroup
 import io.legado.app.lib.dialogs.alert
-import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.cover.Cover
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
 import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
 import io.legado.app.ui.widget.components.text.AppText
@@ -55,11 +53,12 @@ import java.io.FileOutputStream
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun GroupEditSheet(
+    show: Boolean,
     group: BookGroup? = null,
     onDismissRequest: () -> Unit,
     viewModel: GroupViewModel = koinViewModel()
 ) {
-    GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
+    AppModalBottomSheet(show = show, onDismissRequest = onDismissRequest) {
         GroupEditContent(
             group = group,
             onDismissRequest = onDismissRequest,
@@ -108,17 +107,9 @@ fun GroupEditContent(
     Column(
         modifier = Modifier
             .fillMaxWidth()
-            .padding(horizontal = 16.dp)
-            .padding(bottom = 16.dp)
             .verticalScroll(rememberScrollState()),
-        horizontalAlignment = Alignment.CenterHorizontally
+        horizontalAlignment = Alignment.CenterHorizontally,
     ) {
-        AppText(
-            text = stringResource(R.string.group_edit),
-            style = LegadoTheme.typography.titleLarge,
-            modifier = Modifier.padding(vertical = 16.dp)
-        )
-
         Row(
             modifier = Modifier.fillMaxWidth(),
             verticalAlignment = Alignment.Top,

+ 36 - 37
app/src/main/java/io/legado/app/ui/book/import/remote/RemoteBookScreen.kt

@@ -88,7 +88,7 @@ import io.legado.app.ui.widget.components.button.SmallTonalIconButton
 import io.legado.app.ui.widget.components.card.TextCard
 import io.legado.app.ui.widget.components.list.ListScaffold
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.ArchiveUtils
 import io.legado.app.utils.ConvertUtils
@@ -199,42 +199,41 @@ fun RemoteBookScreen(
         }
     }
 
-    if (showSheet != null) {
-        GlassModalBottomSheet(
-            onDismissRequest = { showSheet = null }
-        ) {
-            when (val state = showSheet) {
-                is RemoteBookSheet.Servers -> {
-                    ServersSheetContent(
-                        servers = uiState.servers,
-                        selectedServerId = uiState.selectedServerId,
-                        onSelect = {
-                            viewModel.selectServer(it)
-                            showSheet = null
-                        },
-                        onEdit = { showSheet = RemoteBookSheet.ServerConfig(it) },
-                        onDelete = { viewModel.deleteServer(it) },
-                        onAdd = { showSheet = RemoteBookSheet.ServerConfig(null) },
-                        onDefault = {
-                            viewModel.selectServer(AppConst.DEFAULT_WEBDAV_ID)
-                            showSheet = null
-                        }
-                    )
-                }
-
-                is RemoteBookSheet.ServerConfig -> {
-                    ServerConfigSheetContent(
-                        server = state.server,
-                        onSave = {
-                            viewModel.saveServer(it)
-                            showSheet = RemoteBookSheet.Servers
-                        },
-                        onCancel = { showSheet = RemoteBookSheet.Servers }
-                    )
-                }
+    AppModalBottomSheet(
+        show = showSheet != null,
+        onDismissRequest = { showSheet = null }
+    ) {
+        when (val state = showSheet) {
+            is RemoteBookSheet.Servers -> {
+                ServersSheetContent(
+                    servers = uiState.servers,
+                    selectedServerId = uiState.selectedServerId,
+                    onSelect = {
+                        viewModel.selectServer(it)
+                        showSheet = null
+                    },
+                    onEdit = { showSheet = RemoteBookSheet.ServerConfig(it) },
+                    onDelete = { viewModel.deleteServer(it) },
+                    onAdd = { showSheet = RemoteBookSheet.ServerConfig(null) },
+                    onDefault = {
+                        viewModel.selectServer(AppConst.DEFAULT_WEBDAV_ID)
+                        showSheet = null
+                    }
+                )
+            }
 
-                else -> {}
+            is RemoteBookSheet.ServerConfig -> {
+                ServerConfigSheetContent(
+                    server = state.server,
+                    onSave = {
+                        viewModel.saveServer(it)
+                        showSheet = RemoteBookSheet.Servers
+                    },
+                    onCancel = { showSheet = RemoteBookSheet.Servers }
+                )
             }
+
+            else -> {}
         }
     }
 
@@ -260,7 +259,7 @@ fun RemoteBookScreen(
         },
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
-                text = { AppText("按名称排序") },
+                text = "按名称排序",
                 onClick = {
                     viewModel.toggleSort(RemoteBookSort.Name)
                     dismiss()
@@ -272,7 +271,7 @@ fun RemoteBookScreen(
                 }
             )
             RoundDropdownMenuItem(
-                text = { AppText("按时间排序") },
+                text = "按时间排序",
                 onClick = {
                     viewModel.toggleSort(RemoteBookSort.Default)
                     dismiss()

+ 54 - 56
app/src/main/java/io/legado/app/ui/book/readRecord/ReadRecordScreen.kt

@@ -32,13 +32,9 @@ import androidx.compose.material.icons.filled.Schedule
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material.icons.filled.Timeline
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.Card
-import androidx.compose.material3.CardDefaults
 import androidx.compose.material3.Checkbox
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.SnackbarHost
 import androidx.compose.material3.SnackbarHostState
@@ -78,13 +74,14 @@ import cn.hutool.core.date.DateUtil
 import io.legado.app.data.entities.readRecord.ReadRecord
 import io.legado.app.data.entities.readRecord.ReadRecordDetail
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.AnimatedTextLine
+import io.legado.app.ui.widget.CollapsibleHeader
 import io.legado.app.ui.widget.components.AppScaffold
 import io.legado.app.ui.widget.components.EmptyMessageView
 import io.legado.app.ui.widget.components.SearchBarSection
-import io.legado.app.ui.widget.components.SectionHeader
 import io.legado.app.ui.widget.components.button.AlertButton
+import io.legado.app.ui.widget.components.button.AppIconButton
 import io.legado.app.ui.widget.components.button.TopbarNavigationButton
+import io.legado.app.ui.widget.components.card.GlassCard
 import io.legado.app.ui.widget.components.cover.Cover
 import io.legado.app.ui.widget.components.heatmap.HeatmapCalendarTopBar
 import io.legado.app.ui.widget.components.heatmap.HeatmapConfig
@@ -96,9 +93,11 @@ import io.legado.app.ui.widget.components.heatmap.WeekdayLabelsColumn
 import io.legado.app.ui.widget.components.heatmap.rememberDateRange
 import io.legado.app.ui.widget.components.heatmap.rememberDaysInRange
 import io.legado.app.ui.widget.components.heatmap.rememberWeeks
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.icon.AppIcon
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.swipe.SwipeAction
 import io.legado.app.ui.widget.components.swipe.SwipeActionContainer
+import io.legado.app.ui.widget.components.text.AnimatedTextLine
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar
 import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
@@ -164,7 +163,7 @@ fun ReadRecordScreen(
                         TopbarNavigationButton(onClick = onBackClick)
                     },
                     actions = {
-                        IconButton(onClick = {
+                        AppIconButton(onClick = {
                             val newMode = when (displayMode) {
                                 DisplayMode.AGGREGATE -> DisplayMode.TIMELINE
                                 DisplayMode.TIMELINE -> DisplayMode.LATEST
@@ -178,13 +177,16 @@ fun ReadRecordScreen(
                                 DisplayMode.LATEST -> Icons.AutoMirrored.Filled.List
                             }
                             val description = if (displayMode == DisplayMode.AGGREGATE) "Switch to Timeline" else "Switch to Aggregate"
-                            Icon(icon, description)
+                            AppIcon(icon, description)
                         }
-                        IconButton(onClick = { showCalendar = !showCalendar }) {
-                            Icon(Icons.Default.CalendarMonth, contentDescription = "Toggle Calendar")
+                        AppIconButton(onClick = { showCalendar = !showCalendar }) {
+                            AppIcon(
+                                Icons.Default.CalendarMonth,
+                                contentDescription = "Toggle Calendar"
+                            )
                         }
-                        IconButton(onClick = { showSearch = !showSearch }) {
-                            Icon(Icons.Default.Search, contentDescription = null)
+                        AppIconButton(onClick = { showSearch = !showSearch }) {
+                            AppIcon(Icons.Default.Search, contentDescription = null)
                         }
                     },
                     scrollBehavior = scrollBehavior
@@ -314,24 +316,23 @@ fun ReadRecordScreen(
         )
     }
 
-    if (showCalendar) {
-        GlassModalBottomSheet(
-            onDismissRequest = { showCalendar = false }
-        ) {
-            HeatmapCalendarSection(
-                dailyReadCounts = state.dailyReadCounts,
-                dailyReadTimes = state.dailyReadTimes,
-                selectedDate = state.selectedDate,
-                onDateSelected = { date ->
-                    viewModel.setSelectedDate(date)
-                    showCalendar = false
-                },
-                onClearDate = {
-                    viewModel.setSelectedDate(null)
-                    showCalendar = false
-                }
-            )
-        }
+    AppModalBottomSheet(
+        show = showCalendar,
+        onDismissRequest = { showCalendar = false }
+    ) {
+        HeatmapCalendarSection(
+            dailyReadCounts = state.dailyReadCounts,
+            dailyReadTimes = state.dailyReadTimes,
+            selectedDate = state.selectedDate,
+            onDateSelected = { date ->
+                viewModel.setSelectedDate(date)
+                showCalendar = false
+            },
+            onClearDate = {
+                viewModel.setSelectedDate(null)
+                showCalendar = false
+            }
+        )
     }
 
 
@@ -869,28 +870,27 @@ fun DateHeader(
     date: String,
     dailyTotalTime: Long? = null
 ) {
-    val dateText = formatFriendlyDate(date)
-    SectionHeader(
+    CollapsibleHeader(
+        showIcon = false,
+        isCollapsed = false,
+        onToggle = { },
+        title = "",
         titleContent = {
-            Column {
+            val dateText = formatFriendlyDate(date)
+            AppText(
+                text = dateText,
+                style = LegadoTheme.typography.bodyMedium,
+                fontWeight = FontWeight.Bold,
+                color = LegadoTheme.colorScheme.primary
+            )
+            dailyTotalTime?.let { total ->
                 AppText(
-                    text = dateText,
-                    style = LegadoTheme.typography.bodyMedium,
-                    fontWeight = FontWeight.Bold,
-                    color = MaterialTheme.colorScheme.secondary
+                    text = "已读 ${formatDuring(total)}",
+                    style = LegadoTheme.typography.bodySmall,
+                    color = LegadoTheme.colorScheme.onSurface
                 )
-
-                dailyTotalTime?.let { total ->
-                    AppText(
-                        text = "已读 ${formatDuring(total)}",
-                        style = LegadoTheme.typography.bodySmall,
-                        color = MaterialTheme.colorScheme.onSurface
-                    )
-                }
             }
-        },
-        detailContent = null,
-        horizontalArrangement = Arrangement.Start
+        }
     )
 }
 
@@ -912,14 +912,12 @@ fun ReadingSummaryCard(
 
     val totalDurationMinutes = totalTimeMillis / 60000
 
-    Card(
+    GlassCard(
         onClick = onClick,
         modifier = Modifier
             .fillMaxWidth()
             .padding(horizontal = 16.dp, vertical = 8.dp),
-        colors = CardDefaults.cardColors(
-            containerColor = MaterialTheme.colorScheme.surfaceContainer
-        )
+        containerColor = LegadoTheme.colorScheme.surfaceContainer
     ) {
         Row(
             modifier = Modifier
@@ -934,7 +932,7 @@ fun ReadingSummaryCard(
                 AppText(
                     text = title,
                     style = LegadoTheme.typography.labelLarge,
-                    color = MaterialTheme.colorScheme.primary
+                    color = LegadoTheme.colorScheme.primary
                 )
 
                 Spacer(modifier = Modifier.height(4.dp))
@@ -947,7 +945,7 @@ fun ReadingSummaryCard(
                     AppText(
                         text = "$bookCount",
                         style = LegadoTheme.typography.headlineMedium,
-                        color = MaterialTheme.colorScheme.primary,
+                        color = LegadoTheme.colorScheme.primary,
                         fontWeight = FontWeight.Bold,
                     )
                     AppText(
@@ -965,7 +963,7 @@ fun ReadingSummaryCard(
                 AppText(
                     text = "共阅读 $timeString",
                     style = LegadoTheme.typography.bodySmall,
-                    color = MaterialTheme.colorScheme.onSurfaceVariant
+                    color = LegadoTheme.colorScheme.onSurfaceVariant
                 )
             }
 

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentScreen.kt

@@ -310,7 +310,7 @@ fun SearchHistoryList(
             AppText(
                 text = "搜索历史",
                 style = LegadoTheme.typography.titleSmallEmphasized,
-                color = MaterialTheme.colorScheme.primary,
+                color = LegadoTheme.colorScheme.primary,
                 modifier = Modifier.align(Alignment.Center)
             )
             SmallAnimatedActionButton(

+ 25 - 26
app/src/main/java/io/legado/app/ui/book/toc/TocScreen.kt

@@ -313,33 +313,33 @@ fun TocScreen(
                     when (pagerState.currentPage) {
                         0 -> {
                             RoundDropdownMenuItem(
-                                text = { AppText("使用替换规则") },
+                                text = "使用替换规则",
                                 trailingIcon = {
                                     Checkbox(checked = useReplace, onCheckedChange = null)
                                 },
                                 onClick = { viewModel.toggleUseReplace() }
                             )
                             RoundDropdownMenuItem(
-                                text = { AppText("显示字数") },
+                                text = "显示字数",
                                 trailingIcon = {
                                     Checkbox(checked = showWordCount, onCheckedChange = null)
                                 },
                                 onClick = { viewModel.toggleShowWordCount() }
                             )
                             RoundDropdownMenuItem(
-                                text = { AppText("反转目录") },
+                                text = "反转目录",
                                 onClick = { viewModel.reverseToc() }
                             )
                             PillDivider()
                             RoundDropdownMenuItem(
-                                text = { AppText("替换规则") },
+                                text = "替换规则",
                                 onClick = {
                                     onOpenReplaceRule(null)
                                     dismiss()
                                 }
                             )
                             RoundDropdownMenuItem(
-                                text = { AppText("新建替换规则") },
+                                text = "新建替换规则",
                                 onClick = {
                                     val scopes = mutableListOf<String>()
                                     book?.name?.let { scopes.add(it) }
@@ -359,7 +359,7 @@ fun TocScreen(
                             if (book?.isLocal == true) {
                                 PillHeaderDivider(title = "本地书籍选项")
                                 RoundDropdownMenuItem(
-                                    text = { AppText("本地书籍目录规则") },
+                                    text = "本地书籍目录规则",
                                     onClick = {
                                         val intent =
                                             Intent(context, TxtTocRuleActivity::class.java).apply {
@@ -370,7 +370,7 @@ fun TocScreen(
                                     }
                                 )
                                 RoundDropdownMenuItem(
-                                    text = { AppText("拆分超长章节") },
+                                    text = "拆分超长章节",
                                     trailingIcon = {
                                         Checkbox(
                                             checked = viewModel.isSplitLongChapter,
@@ -387,7 +387,7 @@ fun TocScreen(
 
                         else -> {
                             RoundDropdownMenuItem(
-                                text = { AppText("导出书签为JSON") },
+                                text = "导出书签为JSON",
                                 onClick = {
                                     val dateFormat = SimpleDateFormat(
                                         "yyyyMMdd_HHmm",
@@ -399,7 +399,7 @@ fun TocScreen(
                                 }
                             )
                             RoundDropdownMenuItem(
-                                text = { AppText("导出书签为MarkDown") },
+                                text = "导出书签为MarkDown",
                                 onClick = {
                                     val dateFormat = SimpleDateFormat(
                                         "yyyyMMdd_HHmm",
@@ -465,13 +465,13 @@ fun TocScreen(
                                     onDismissRequest = { showVolumeMenu = false }
                                 ) {
                                     RoundDropdownMenuItem(
-                                        text = { AppText("展开所有卷") },
+                                        text = "展开所有卷",
                                         onClick = {
                                             viewModel.expandAllVolumes(); showVolumeMenu = false
                                         }
                                     )
                                     RoundDropdownMenuItem(
-                                        text = { AppText("收起所有卷") },
+                                        text = "收起所有卷",
                                         onClick = {
                                             viewModel.collapseAllVolumes(); showVolumeMenu = false
                                         }
@@ -483,7 +483,7 @@ fun TocScreen(
                                         PillHeaderDivider(title = "快速跳转")
                                         volumeItems.forEach { uiItem ->
                                             RoundDropdownMenuItem(
-                                                text = { AppText(uiItem.title) },
+                                                text = uiItem.title,
                                                 onClick = {
                                                     scope.launch {
                                                         val targetIndex =
@@ -594,20 +594,19 @@ fun TocScreen(
             }
         }
 
-        if (editingBookmark != null) {
-            BookmarkEditSheet(
-                bookmark = editingBookmark!!,
-                onDismiss = { editingBookmark = null },
-                onSave = { updatedBookmark ->
-                    viewModel.updateBookmark(updatedBookmark)
-                    editingBookmark = null
-                },
-                onDelete = { bookmarkToDelete ->
-                    viewModel.deleteBookmark(bookmarkToDelete)
-                    editingBookmark = null
-                }
-            )
-        }
+        BookmarkEditSheet(
+            show = editingBookmark != null,
+            bookmark = editingBookmark!!,
+            onDismiss = { editingBookmark = null },
+            onSave = { updatedBookmark ->
+                viewModel.updateBookmark(updatedBookmark)
+                editingBookmark = null
+            },
+            onDelete = { bookmarkToDelete ->
+                viewModel.deleteBookmark(bookmarkToDelete)
+                editingBookmark = null
+            }
+        )
     }
 }
 

+ 82 - 91
app/src/main/java/io/legado/app/ui/book/toc/rule/TxtRuleScreen.kt

@@ -5,7 +5,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
@@ -44,13 +43,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import io.legado.app.R
 import io.legado.app.base.BaseRuleEvent
 import io.legado.app.data.entities.TxtTocRule
-import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.DraggableSelectionHandler
 import io.legado.app.ui.widget.components.button.SmallIconButton
 import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
 import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
-import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
 import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
 import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
 import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
@@ -154,54 +151,49 @@ fun TxtRuleScreen(
         )
     }
 
-    if (showExportSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showExportSheet = false },
-            title = stringResource(R.string.export),
-            onSelectSysDir = {
-                showExportSheet = false
-                exportDoc.launch("exportDictRule.json")
-            },
-            onUpload = {
-                showExportSheet = false
-                viewModel.uploadSelectedRules(selectedIds, rules)
-            },
-            allowExtensions = arrayOf("json")
-        )
-    }
+    FilePickerSheet(
+        show = showExportSheet,
+        onDismissRequest = { showExportSheet = false },
+        title = stringResource(R.string.export),
+        onSelectSysDir = {
+            showExportSheet = false
+            exportDoc.launch("exportDictRule.json")
+        },
+        onUpload = {
+            showExportSheet = false
+            viewModel.uploadSelectedRules(selectedIds, rules)
+        },
+        allowExtensions = arrayOf("json")
+    )
 
-    if (showImportSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showImportSheet = false },
-            title = stringResource(R.string.import_txt_toc_rule),
-            onSelectSysFile = { types ->
-                importDoc.launch(types)
-                showImportSheet = false
-            },
-            onManualInput = {
-                showUrlInput = true
-                showImportSheet = false
-            },
-            allowExtensions = arrayOf("json", "txt")
-        )
-    }
 
-    (importState as? BaseImportUiState.Success<TxtTocRule>)?.let { state ->
-        BatchImportDialog(
-            title = "导入词典规则",
-            importState = state,
-            onDismissRequest = { viewModel.cancelImport() },
-            onToggleItem = { viewModel.toggleImportSelection(it) },
-            onToggleAll = { viewModel.toggleImportAll(it) },
-            onConfirm = { viewModel.saveImportedRules() },
-            itemContent = { rule, _ ->
-                Column {
-                    AppText(rule.name, style = LegadoTheme.typography.titleMedium)
-                    AppText(rule.rule, style = LegadoTheme.typography.bodySmall, maxLines = 1)
-                }
-            }
-        )
-    }
+    FilePickerSheet(
+        show = showImportSheet,
+        onDismissRequest = { showImportSheet = false },
+        title = stringResource(R.string.import_txt_toc_rule),
+        onSelectSysFile = { types ->
+            importDoc.launch(types)
+            showImportSheet = false
+        },
+        onManualInput = {
+            showUrlInput = true
+            showImportSheet = false
+        },
+        allowExtensions = arrayOf("json", "txt")
+    )
+
+    BatchImportDialog(
+        title = "导入词典规则",
+        importState = importState,
+        onDismissRequest = { viewModel.cancelImport() },
+        onToggleItem = { viewModel.toggleImportSelection(it) },
+        onToggleAll = { viewModel.toggleImportAll(it) },
+        onConfirm = { viewModel.saveImportedRules() },
+        itemTitle = { rule -> rule.name },
+        itemSubtitle = { rule ->
+            rule.rule.takeIf { it.isNotBlank() }
+        }
+    )
 
     LaunchedEffect(reorderableState.isAnyItemDragging) {
         if (!reorderableState.isAnyItemDragging) {
@@ -229,48 +221,47 @@ fun TxtRuleScreen(
         )
     }
 
-    if (showEditSheet) {
-        RuleEditSheet(
-            rule = editingRule,
-            title = stringResource(R.string.txt_toc_rule),
-            label1 = stringResource(R.string.regex),
-            label2 = stringResource(R.string.example),
-            onDismissRequest = {
-                showEditSheet = false
-                editingRule = null
-            },
-            onSave = { updatedRule ->
-                //TODO:我很想把他改为自增主键,但为了兼容性日后再说
-                if (editingRule == null) {
-                    viewModel.insert(updatedRule)
-                } else {
-                    viewModel.update(updatedRule)
-                }
-                showEditSheet = false
-                editingRule = null
-            },
-            onCopy = { viewModel.copyRule(it) },
-            onPaste = { viewModel.pasteRule() },
-            toFields = { r ->
-                RuleEditFields(
-                    name = r?.name ?: "",
-                    rule1 = r?.rule ?: "",
-                    rule2 = r?.example ?: ""
-                )
-            },
-            fromFields = { fields, old ->
-                old?.copy(
-                    name = fields.name,
-                    rule = fields.rule1,
-                    example = fields.rule2
-                ) ?: TxtTocRule(
-                    name = fields.name,
-                    rule = fields.rule1,
-                    example = fields.rule2
-                )
+    RuleEditSheet(
+        show = showEditSheet,
+        rule = editingRule,
+        title = stringResource(R.string.txt_toc_rule),
+        label1 = stringResource(R.string.regex),
+        label2 = stringResource(R.string.example),
+        onDismissRequest = {
+            showEditSheet = false
+            editingRule = null
+        },
+        onSave = { updatedRule ->
+            //TODO:我很想把他改为自增主键,但为了兼容性日后再说
+            if (editingRule == null) {
+                viewModel.insert(updatedRule)
+            } else {
+                viewModel.update(updatedRule)
             }
-        )
-    }
+            showEditSheet = false
+            editingRule = null
+        },
+        onCopy = { viewModel.copyRule(it) },
+        onPaste = { viewModel.pasteRule() },
+        toFields = { r ->
+            RuleEditFields(
+                name = r?.name ?: "",
+                rule1 = r?.rule ?: "",
+                rule2 = r?.example ?: ""
+            )
+        },
+        fromFields = { fields, old ->
+            old?.copy(
+                name = fields.name,
+                rule = fields.rule1,
+                example = fields.rule2
+            ) ?: TxtTocRule(
+                name = fields.name,
+                rule = fields.rule1,
+                example = fields.rule2
+            )
+        }
+    )
 
     RuleListScaffold(
         title = if (isPickMode) "选择目录规则" else "目录规则",
@@ -312,7 +303,7 @@ fun TxtRuleScreen(
         snackbarHostState = snackbarHostState,
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.import_str)) },
+                text = stringResource(R.string.import_str),
                 onClick = { showImportSheet = true; dismiss() },
                 leadingIcon = { Icon(Icons.Default.FileOpen, null) }
             )

+ 21 - 22
app/src/main/java/io/legado/app/ui/config/backupConfig/BackupConfigScreen.kt

@@ -453,31 +453,30 @@ fun BackupConfigScreen(
         )
     }
 
-    if (showBackupFilePicker) {
-        FilePickerSheet(
-            onDismissRequest = { showBackupFilePicker = false },
-            onSelectSysDir = {
-                showBackupFilePicker = false
-                try {
-                    selectBackupPathLauncher.launch(null)
-                } catch (e: Exception) {
-                }
+    FilePickerSheet(
+        show = showBackupFilePicker,
+        onDismissRequest = { showBackupFilePicker = false },
+        onSelectSysDir = {
+            showBackupFilePicker = false
+            try {
+                selectBackupPathLauncher.launch(null)
+            } catch (e: Exception) {
             }
-        )
-    }
+        }
+    )
 
-    if (showRestoreFilePicker) {
-        FilePickerSheet(
-            onDismissRequest = { showRestoreFilePicker = false },
-            onSelectSysDir = {
-                showRestoreFilePicker = false
-                try {
-                    backupAndSelectLauncher.launch(null)
-                } catch (e: Exception) {
-                }
+
+    FilePickerSheet(
+        show = showRestoreFilePicker,
+        onDismissRequest = { showRestoreFilePicker = false },
+        onSelectSysDir = {
+            showRestoreFilePicker = false
+            try {
+                backupAndSelectLauncher.launch(null)
+            } catch (e: Exception) {
             }
-        )
-    }
+        }
+    )
 
     if (showRestoreSheet && backupNames.isNotEmpty()) {
         AlertDialog(

+ 11 - 10
app/src/main/java/io/legado/app/ui/config/coverConfig/CoverConfigScreen.kt

@@ -246,17 +246,17 @@ fun CoverConfigScreen(
         }
     }
 
-    if (showCoverRuleSheet) {
-        CoverRuleConfigSheet(onDismissRequest = { showCoverRuleSheet = false })
-    }
+    CoverRuleConfigSheet(
+        show = showCoverRuleSheet,
+        onDismissRequest = { showCoverRuleSheet = false }
+    )
 
-    manageKey?.let { key ->
-        CoverManageSheet(
-            preferenceKey = key,
-            onDismissRequest = { manageKey = null },
-            viewModel = viewModel
-        )
-    }
+    CoverManageSheet(
+        show = manageKey != null,
+        preferenceKey = manageKey!!,
+        onDismissRequest = { manageKey = null },
+        viewModel = viewModel
+    )
 
     showColorPickerByField?.let { field ->
         val initialColor = when (field) {
@@ -268,6 +268,7 @@ fun CoverConfigScreen(
         }
 
         ColorPickerSheet(
+            show = showColorPickerByField != null,
             initialColor = initialColor,
             onDismissRequest = { showColorPickerByField = null },
             onColorSelected = { color ->

+ 13 - 12
app/src/main/java/io/legado/app/ui/config/coverConfig/CoverManageSheet.kt

@@ -37,12 +37,13 @@ import io.legado.app.R
 import io.legado.app.constant.PreferKey
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun CoverManageSheet(
+    show: Boolean,
     preferenceKey: String,
     onDismissRequest: () -> Unit,
     viewModel: CoverConfigViewModel
@@ -62,7 +63,8 @@ fun CoverManageSheet(
     }
     val coverList = currentCovers.split(",").filter { it.isNotBlank() }
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismissRequest
     ) {
         Column(
@@ -142,14 +144,13 @@ fun CoverManageSheet(
         }
     }
 
-    if (showFilePicker) {
-        FilePickerSheet(
-            onDismissRequest = { showFilePicker = false },
-            onSelectSysFiles = {
-                selectImages.launch("image/*")
-                showFilePicker = false
-            },
-            allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
-        )
-    }
+    FilePickerSheet(
+        show = showFilePicker,
+        onDismissRequest = { showFilePicker = false },
+        onSelectSysFiles = {
+            selectImages.launch("image/*")
+            showFilePicker = false
+        },
+        allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
+    )
 }

+ 4 - 2
app/src/main/java/io/legado/app/ui/config/coverConfig/CoverRuleConfigSheet.kt

@@ -25,7 +25,7 @@ import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.model.BookCover
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.settingItem.InputSettingItem
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.toastOnUi
@@ -36,6 +36,7 @@ import splitties.init.appCtx
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun CoverRuleConfigSheet(
+    show: Boolean,
     onDismissRequest: () -> Unit
 ) {
     var enable by remember { mutableStateOf(false) }
@@ -51,7 +52,8 @@ fun CoverRuleConfigSheet(
         coverRule = rule.coverRule
     }
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismissRequest
     ) {
         Column(

+ 5 - 3
app/src/main/java/io/legado/app/ui/config/otherConfig/CheckSourceBottomSheet.kt

@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.Button
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
-import io.legado.app.ui.widget.components.text.AppText
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
@@ -21,19 +20,22 @@ import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.checkBox.CheckboxGroupContainer
 import io.legado.app.ui.widget.components.checkBox.CheckboxItem
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
+import io.legado.app.ui.widget.components.text.AppText
 import org.koin.androidx.compose.koinViewModel
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun CheckSourceBottomSheet(
+    show: Boolean,
     viewModel: OtherConfigViewModel = koinViewModel(),
     onDismiss: () -> Unit
 ) {
     val context = LocalContext.current
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismiss
     ) {
         Column(

+ 4 - 2
app/src/main/java/io/legado/app/ui/config/otherConfig/DirectLinkUploadBottomSheet.kt

@@ -42,7 +42,7 @@ import io.legado.app.help.DirectLinkUpload
 import io.legado.app.lib.dialogs.selector
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.checkBox.CheckboxItem
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.GSON
 import io.legado.app.utils.fromJsonObject
@@ -53,6 +53,7 @@ import io.legado.app.utils.toastOnUi
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun DirectLinkUploadBottomSheet(
+    show: Boolean,
     viewModel: OtherConfigViewModel,
     onDismiss: () -> Unit
 ) {
@@ -64,7 +65,8 @@ fun DirectLinkUploadBottomSheet(
         viewModel.initDirectLinkRule()
     }
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismiss
     ) {
         Column(

+ 22 - 25
app/src/main/java/io/legado/app/ui/config/otherConfig/OtherConfigScreen.kt

@@ -398,33 +398,30 @@ fun OtherConfigScreen(
             }
         }
 
-        if (showFilePicker) {
-            FilePickerSheet(
-                onDismissRequest = { showFilePicker = false },
-                onSelectSysDir = {
-                    showFilePicker = false
-                    try {
-                        selectDocTree.launch(null)
-                    } catch (e: Exception) {
+        FilePickerSheet(
+            show = showFilePicker,
+            onDismissRequest = { showFilePicker = false },
+            onSelectSysDir = {
+                showFilePicker = false
+                try {
+                    selectDocTree.launch(null)
+                } catch (e: Exception) {
 
-                    }
                 }
-            )
-        }
-
-        if (showCheckSourceSheet) {
-            CheckSourceBottomSheet(
-                viewModel = viewModel,
-                onDismiss = { showCheckSourceSheet = false }
-            )
-        }
-
-        if (showDirectLinkUploadSheet) {
-            DirectLinkUploadBottomSheet(
-                viewModel = viewModel,
-                onDismiss = { showDirectLinkUploadSheet = false }
-            )
-        }
+            }
+        )
+
+        CheckSourceBottomSheet(
+            show = showCheckSourceSheet,
+            viewModel = viewModel,
+            onDismiss = { showCheckSourceSheet = false }
+        )
+
+        DirectLinkUploadBottomSheet(
+            show = showDirectLinkUploadSheet,
+            viewModel = viewModel,
+            onDismiss = { showDirectLinkUploadSheet = false }
+        )
 
         if (showClearCacheDialog) {
             ConfirmDialog(

+ 3 - 2
app/src/main/java/io/legado/app/ui/config/readConfig/PageKeySheet.kt

@@ -23,18 +23,19 @@ import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun PageKeySheet(
+    show: Boolean,
     onDismissRequest: () -> Unit
 ) {
     var prevKeys by remember { mutableStateOf(ReadConfig.prevKeys) }
     var nextKeys by remember { mutableStateOf(ReadConfig.nextKeys) }
 
-    GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
+    AppModalBottomSheet(show = show, onDismissRequest = onDismissRequest) {
         Column(
             modifier = Modifier.Companion
                 .fillMaxWidth()

+ 4 - 3
app/src/main/java/io/legado/app/ui/config/readConfig/ReadConfigScreen.kt

@@ -320,7 +320,8 @@ fun ReadConfigScreen(
         }
     }
 
-    if (showPageKeySheet) {
-        PageKeySheet(onDismissRequest = { showPageKeySheet = false })
-    }
+    PageKeySheet(
+        show = showPageKeySheet,
+        onDismissRequest = { showPageKeySheet = false }
+    )
 }

+ 13 - 12
app/src/main/java/io/legado/app/ui/config/themeConfig/BackgroundImageManageSheet.kt

@@ -32,7 +32,7 @@ import coil.compose.AsyncImage
 import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import kotlinx.coroutines.launch
 import org.koin.androidx.compose.koinViewModel
@@ -40,6 +40,7 @@ import org.koin.androidx.compose.koinViewModel
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun BackgroundImageManageSheet(
+    show: Boolean,
     isDarkTheme: Boolean,
     onDismissRequest: () -> Unit,
     viewModel: ThemeConfigViewModel = koinViewModel()
@@ -65,7 +66,8 @@ fun BackgroundImageManageSheet(
         ThemeConfig.bgImageLight
     }
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismissRequest
     ) {
         Column(
@@ -140,14 +142,13 @@ fun BackgroundImageManageSheet(
         }
     }
 
-    if (showFilePicker) {
-        FilePickerSheet(
-            onDismissRequest = { showFilePicker = false },
-            onSelectSysFile = {
-                selectImage.launch("image/*")
-                showFilePicker = false
-            },
-            allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
-        )
-    }
+    FilePickerSheet(
+        show = showFilePicker,
+        onDismissRequest = { showFilePicker = false },
+        onSelectSysFile = {
+            selectImage.launch("image/*")
+            showFilePicker = false
+        },
+        allowExtensions = arrayOf("jpg", "jpeg", "png", "webp")
+    )
 }

+ 4 - 2
app/src/main/java/io/legado/app/ui/config/themeConfig/LauncherIconPickerSheet.kt

@@ -39,7 +39,7 @@ import io.legado.app.ui.main.Launcher5
 import io.legado.app.ui.main.Launcher6
 import io.legado.app.ui.main.LauncherW
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.getCompatDrawable
 import splitties.init.appCtx
@@ -47,6 +47,7 @@ import splitties.init.appCtx
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun LauncherIconPickerSheet(
+    show: Boolean,
     selectedValue: String,
     onDismissRequest: () -> Unit,
     onValueChange: (String) -> Unit
@@ -54,7 +55,8 @@ fun LauncherIconPickerSheet(
     val context = LocalContext.current
     val icons = LauncherIcons.list
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismissRequest
     ) {
         Column(

+ 2 - 2
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfig.kt

@@ -15,14 +15,14 @@ object ThemeConfig {
 
     var enableBlur by prefDelegate(PreferKey.enableBlur, false)
 
-    var enableProgressiveBlur by prefDelegate(PreferKey.enableProgressiveBlur, true)
+    var enableProgressiveBlur by prefDelegate(PreferKey.enableProgressiveBlur, false)
 
     var useFlexibleTopAppBar by prefDelegate(PreferKey.useFlexibleTopAppBar, true)
 
     var paletteStyle by prefDelegate(PreferKey.paletteStyle, "tonalSpot")
 
     //m3 or miuix
-    var composeEngine by prefDelegate(PreferKey.composeEngine, "m3")
+    var composeEngine by prefDelegate(PreferKey.composeEngine, "material")
 
     var materialVersion by prefDelegate(PreferKey.materialVersion, "material3")
 

+ 61 - 39
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeConfigScreen.kt

@@ -15,6 +15,7 @@ import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
@@ -44,6 +45,7 @@ import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedButton
 import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.material3.ToggleButton
 import androidx.compose.material3.ToggleButtonDefaults
@@ -95,6 +97,10 @@ import io.legado.app.utils.postEvent
 import io.legado.app.utils.restart
 import io.legado.app.utils.toastOnUi
 import org.koin.androidx.compose.koinViewModel
+import top.yukonga.miuix.kmp.theme.MiuixTheme
+import top.yukonga.miuix.kmp.basic.Card as MiuixCard
+import top.yukonga.miuix.kmp.basic.CardDefaults as MiuixCardDefaults
+import top.yukonga.miuix.kmp.basic.Text as MiuixText
 
 @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
 @Composable
@@ -137,9 +143,10 @@ fun ThemeConfigScreen(
                 .verticalScroll(rememberScrollState())
                 .padding(16.dp)
         ) {
-            val composeEngine = remember { ThemeConfig.composeEngine }
-            val isMiuixEngine =
-                remember(composeEngine) { ThemeResolver.isMiuixEngine(composeEngine) }
+            val composeEngine = ThemeConfig.composeEngine
+            val isMiuixEngine = remember(composeEngine) {
+                ThemeResolver.isMiuixEngine(composeEngine)
+            }
             val isDarkTheme = when (selectedThemeMode) {
                 "1" -> false
                 "2" -> true
@@ -169,6 +176,20 @@ fun ThemeConfigScreen(
                 themeItems.zip(themeValues).toList()
             }
 
+            if (isMiuixEngine) {
+                MiuixCard(
+                    cornerRadius = 16.dp,
+                    insideMargin = PaddingValues(16.dp),
+                    colors = MiuixCardDefaults.defaultColors(
+                        color = MiuixTheme.colorScheme.primaryVariant,
+                        contentColor = MiuixTheme.colorScheme.onPrimary
+                    )
+                ) {
+                    MiuixText("Miuix 目前为测试主题,且不对基于View的界面生效!")
+                }
+            }
+
+
             SplicedColumnGroup(title = stringResource(R.string.theme)) {
                 if (isMiuixEngine) {
                     DropdownListSettingItem(
@@ -253,7 +274,9 @@ fun ThemeConfigScreen(
                     selectedValue = ThemeConfig.composeEngine,
                     displayEntries = stringArrayResource(R.array.composeEngine),
                     entryValues = stringArrayResource(R.array.composeEngine_value),
-                    onValueChange = { ThemeConfig.composeEngine = it }
+                    onValueChange = {
+                        ThemeConfig.composeEngine = it
+                    }
                 )
                 SliderSettingItem(
                     title = stringResource(R.string.font_scale),
@@ -531,47 +554,46 @@ fun ThemeConfigScreen(
 
     manageKey?.let { isDark ->
         BackgroundImageManageSheet(
+            show = true,
             isDarkTheme = isDark,
             onDismissRequest = { manageKey = null }
         )
     }
 
-    if (showColorPicker) {
-        ColorPickerSheet(
-            initialColor = primaryColorValue.value,
-            onDismissRequest = { showColorPicker = false },
-            onColorSelected = { color ->
-                primaryColorValue.value = color
-                ThemeConfig.cPrimary = color
-                ThemeStore.editTheme(context)
-                    .primaryColor(color)
-                    .apply()
-                DynamicColors.applyToActivitiesIfAvailable(
-                    context.applicationContext as android.app.Application,
-                    DynamicColorsOptions.Builder()
-                        .setContentBasedSource(context.primaryColor)
-                        .build()
-                )
-            }
-        )
-    }
 
-    if (showLauncherIconPicker) {
-        LauncherIconPickerSheet(
-            selectedValue = ThemeConfig.launcherIcon,
-            onDismissRequest = { showLauncherIconPicker = false },
-            onValueChange = {
-                ThemeConfig.launcherIcon = it
-                LauncherIconHelp.changeIcon(it)
-            }
-        )
-    }
+    ColorPickerSheet(
+        show = showColorPicker,
+        initialColor = primaryColorValue.value,
+        onDismissRequest = { showColorPicker = false },
+        onColorSelected = { color ->
+            primaryColorValue.value = color
+            ThemeConfig.cPrimary = color
+            ThemeStore.editTheme(context)
+                .primaryColor(color)
+                .apply()
+            DynamicColors.applyToActivitiesIfAvailable(
+                context.applicationContext as android.app.Application,
+                DynamicColorsOptions.Builder()
+                    .setContentBasedSource(context.primaryColor)
+                    .build()
+            )
+        }
+    )
 
-    if (showThemeListDialog) {
-        ThemeListDialog(
-            onDismissRequest = { showThemeListDialog = false }
-        )
-    }
+    LauncherIconPickerSheet(
+        show = showLauncherIconPicker,
+        selectedValue = ThemeConfig.launcherIcon,
+        onDismissRequest = { showLauncherIconPicker = false },
+        onValueChange = {
+            ThemeConfig.launcherIcon = it
+            LauncherIconHelp.changeIcon(it)
+        }
+    )
+
+    ThemeListDialog(
+        show = showThemeListDialog,
+        onDismissRequest = { showThemeListDialog = false }
+    )
 
     saveThemeKey?.let { key ->
         AlertDialog(
@@ -652,7 +674,7 @@ fun ThemeModeSelector(
 
                 Spacer(Modifier.size(ToggleButtonDefaults.IconSpacing))
 
-                AppText(text = label)
+                Text(text = label)
             }
         }
     }

+ 4 - 2
app/src/main/java/io/legado/app/ui/config/themeConfig/ThemeListDialog.kt

@@ -38,7 +38,7 @@ import io.legado.app.help.config.OldThemeConfig
 import io.legado.app.lib.theme.primaryColor
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.card.GlassCard
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.GSON
 import io.legado.app.utils.getClipText
@@ -48,6 +48,7 @@ import io.legado.app.utils.toastOnUi
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun ThemeListDialog(
+    show: Boolean,
     onDismissRequest: () -> Unit
 ) {
     val context = LocalContext.current
@@ -55,7 +56,8 @@ fun ThemeListDialog(
     var deleteIndex by remember { mutableStateOf<Int?>(null) }
     val themeList = remember(listVersion) { OldThemeConfig.configList.toList() }
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismissRequest
     ) {
         Column(

+ 3 - 2
app/src/main/java/io/legado/app/ui/dict/rule/DictRuleEditSheet.kt

@@ -36,7 +36,7 @@ import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.data.entities.DictRule
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import kotlinx.coroutines.launch
 
@@ -56,7 +56,8 @@ fun DictRuleEditSheet(
     var showRule by remember(rule) { mutableStateOf(rule?.showRule ?: "") }
     var showMenu by remember { mutableStateOf(false) }
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = true,
         onDismissRequest = onDismissRequest
     ) {
         Box(

+ 81 - 90
app/src/main/java/io/legado/app/ui/dict/rule/DictRuleScreen.kt

@@ -5,7 +5,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
@@ -44,13 +43,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import io.legado.app.R
 import io.legado.app.base.BaseRuleEvent
 import io.legado.app.data.entities.DictRule
-import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.DraggableSelectionHandler
 import io.legado.app.ui.widget.components.button.SmallIconButton
 import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
 import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
-import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
 import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
 import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
 import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
@@ -153,54 +150,49 @@ fun DictRuleScreen(
         )
     }
 
-    if (showExportSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showExportSheet = false },
-            title = stringResource(R.string.export),
-            onSelectSysDir = {
-                showExportSheet = false
-                exportDoc.launch("exportDictRule.json")
-            },
-            onUpload = {
-                showExportSheet = false
-                viewModel.uploadSelectedRules(selectedIds, rules)
-            },
-            allowExtensions = arrayOf("json")
-        )
-    }
+    FilePickerSheet(
+        show = showExportSheet,
+        onDismissRequest = { showExportSheet = false },
+        title = stringResource(R.string.export),
+        onSelectSysDir = {
+            showExportSheet = false
+            exportDoc.launch("exportDictRule.json")
+        },
+        onUpload = {
+            showExportSheet = false
+            viewModel.uploadSelectedRules(selectedIds, rules)
+        },
+        allowExtensions = arrayOf("json")
+    )
 
-    if (showImportSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showImportSheet = false },
-            title = stringResource(R.string.import_dict_rule),
-            onSelectSysFile = { types ->
-                importDoc.launch(types)
-                showImportSheet = false
-            },
-            onManualInput = {
-                showUrlInput = true
-                showImportSheet = false
-            },
-            allowExtensions = arrayOf("json", "txt")
-        )
-    }
 
-    (importState as? BaseImportUiState.Success<DictRule>)?.let { state ->
-        BatchImportDialog(
-            title = "导入词典规则",
-            importState = state,
-            onDismissRequest = { viewModel.cancelImport() },
-            onToggleItem = { viewModel.toggleImportSelection(it) },
-            onToggleAll = { viewModel.toggleImportAll(it) },
-            onConfirm = { viewModel.saveImportedRules() },
-            itemContent = { rule, _ ->
-                Column {
-                    AppText(rule.name, style = LegadoTheme.typography.titleMedium)
-                    AppText(rule.urlRule, style = LegadoTheme.typography.bodySmall, maxLines = 1)
-                }
-            }
-        )
-    }
+    FilePickerSheet(
+        show = showImportSheet,
+        onDismissRequest = { showImportSheet = false },
+        title = stringResource(R.string.import_dict_rule),
+        onSelectSysFile = { types ->
+            importDoc.launch(types)
+            showImportSheet = false
+        },
+        onManualInput = {
+            showUrlInput = true
+            showImportSheet = false
+        },
+        allowExtensions = arrayOf("json", "txt")
+    )
+
+    BatchImportDialog(
+        title = "导入词典规则",
+        importState = importState,
+        onDismissRequest = { viewModel.cancelImport() },
+        onToggleItem = { viewModel.toggleImportSelection(it) },
+        onToggleAll = { viewModel.toggleImportAll(it) },
+        onConfirm = { viewModel.saveImportedRules() },
+        itemTitle = { rule -> rule.name },
+        itemSubtitle = { rule ->
+            rule.urlRule.takeIf { it.isNotBlank() }
+        }
+    )
 
     LaunchedEffect(reorderableState.isAnyItemDragging) {
         if (!reorderableState.isAnyItemDragging) {
@@ -228,47 +220,46 @@ fun DictRuleScreen(
         )
     }
 
-    if (showEditSheet) {
-        RuleEditSheet(
-            rule = editingRule,
-            title = stringResource(R.string.dict_rule),
-            label1 = stringResource(R.string.url_rule),
-            label2 = stringResource(R.string.show_rule),
-            onDismissRequest = {
-                showEditSheet = false
-                editingRule = null
-            },
-            onSave = { updatedRule ->
-                if (editingRule == null) {
-                    viewModel.insert(updatedRule)
-                } else {
-                    viewModel.update(updatedRule)
-                }
-                showEditSheet = false
-                editingRule = null
-            },
-            onCopy = { viewModel.copyRule(it) },
-            onPaste = { viewModel.pasteRule() },
-            toFields = { r ->
-                RuleEditFields(
-                    name = r?.name ?: "",
-                    rule1 = r?.urlRule ?: "",
-                    rule2 = r?.showRule ?: ""
-                )
-            },
-            fromFields = { fields, old ->
-                old?.copy(
-                    name = fields.name,
-                    urlRule = fields.rule1,
-                    showRule = fields.rule2
-                ) ?: DictRule(
-                    name = fields.name,
-                    urlRule = fields.rule1,
-                    showRule = fields.rule2
-                )
+    RuleEditSheet(
+        show = showEditSheet,
+        rule = editingRule,
+        title = stringResource(R.string.dict_rule),
+        label1 = stringResource(R.string.url_rule),
+        label2 = stringResource(R.string.show_rule),
+        onDismissRequest = {
+            showEditSheet = false
+            editingRule = null
+        },
+        onSave = { updatedRule ->
+            if (editingRule == null) {
+                viewModel.insert(updatedRule)
+            } else {
+                viewModel.update(updatedRule)
             }
-        )
-    }
+            showEditSheet = false
+            editingRule = null
+        },
+        onCopy = { viewModel.copyRule(it) },
+        onPaste = { viewModel.pasteRule() },
+        toFields = { r ->
+            RuleEditFields(
+                name = r?.name ?: "",
+                rule1 = r?.urlRule ?: "",
+                rule2 = r?.showRule ?: ""
+            )
+        },
+        fromFields = { fields, old ->
+            old?.copy(
+                name = fields.name,
+                urlRule = fields.rule1,
+                showRule = fields.rule2
+            ) ?: DictRule(
+                name = fields.name,
+                urlRule = fields.rule1,
+                showRule = fields.rule2
+            )
+        }
+    )
 
     RuleListScaffold(
         title = "字典规则",
@@ -310,7 +301,7 @@ fun DictRuleScreen(
         snackbarHostState = snackbarHostState,
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.import_str)) },
+                text = stringResource(R.string.import_str),
                 onClick = { showImportSheet = true; dismiss() },
                 leadingIcon = { Icon(Icons.Default.FileOpen, null) }
             )

+ 3 - 2
app/src/main/java/io/legado/app/ui/main/MainScreen.kt

@@ -26,6 +26,7 @@ import androidx.compose.material3.ExtendedFloatingActionButton
 import androidx.compose.material3.Icon
 import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
 import androidx.compose.material3.WideNavigationRail
 import androidx.compose.material3.WideNavigationRailItem
 import androidx.compose.material3.WideNavigationRailValue
@@ -195,7 +196,7 @@ fun MainScreen(
                                     ) { dismiss ->
                                         bookshelfUiState.groups.forEachIndexed { groupIndex, group ->
                                             RoundDropdownMenuItem(
-                                                text = { AppText(group.groupName) },
+                                                text = group.groupName,
                                                 onClick = {
                                                     coroutineScope.launch {
                                                         if (pagerState.currentPage != index) {
@@ -321,7 +322,7 @@ private fun NavigationIcon(
     val icon = if (selected) destination.m3SelectedIcon else destination.m3Icon
     Box(modifier = modifier) {
         if (destination == MainDestination.Bookshelf && upBooksCount > 0) {
-            BadgedBox(badge = { Badge { AppText(upBooksCount.toString()) } }) {
+            BadgedBox(badge = { Badge { Text(upBooksCount.toString()) } }) {
                 Icon(icon, contentDescription = null)
             }
         } else {

+ 11 - 12
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfConfigSheet.kt

@@ -31,38 +31,37 @@ import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.ui.config.bookshelfConfig.BookshelfConfig
-import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.card.GlassCard
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.settingItem.CompactDropdownSettingItem
 import io.legado.app.ui.widget.components.settingItem.CompactSliderSettingItem
 import io.legado.app.ui.widget.components.settingItem.CompactSwitchSettingItem
-import io.legado.app.ui.widget.components.text.AppText
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun BookshelfConfigSheet(
+    show: Boolean,
     onDismissRequest: () -> Unit
 ) {
     val configuration = LocalConfiguration.current
     val isLandscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
 
-    GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
+    AppModalBottomSheet(
+        title = stringResource(R.string.bookshelf_layout),
+        show = show,
+        onDismissRequest = onDismissRequest
+    ) {
+        GlassCard() {
+
+        }
         Column(
             modifier = Modifier
                 .fillMaxWidth()
                 .verticalScroll(rememberScrollState())
                 .animateContentSize()
-                .padding(horizontal = 16.dp)
                 .padding(bottom = 32.dp),
             verticalArrangement = Arrangement.spacedBy(8.dp)
         ) {
-            AppText(
-                text = stringResource(R.string.bookshelf_layout),
-                style = LegadoTheme.typography.titleLarge,
-                modifier = Modifier.padding(vertical = 16.dp)
-            )
-
-            // Group Style
             CompactDropdownSettingItem(
                 title = stringResource(R.string.group_style),
                 selectedValue = BookshelfConfig.bookGroupStyle.toString(),

+ 54 - 57
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt

@@ -40,12 +40,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.PrimaryScrollableTabRow
 import androidx.compose.material3.SnackbarHostState
 import androidx.compose.material3.SnackbarResult
 import androidx.compose.material3.Surface
-import androidx.compose.material3.Tab
-import io.legado.app.ui.widget.components.text.AppText
 import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
 import androidx.compose.material3.pulltorefresh.pullToRefresh
 import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
@@ -60,13 +57,11 @@ import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.ClipEntry
 import androidx.compose.ui.platform.LocalClipboard
 import androidx.compose.ui.platform.LocalConfiguration
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.window.Dialog
 import io.legado.app.R
@@ -86,7 +81,8 @@ import io.legado.app.ui.widget.components.lazylist.FastScrollLazyVerticalGrid
 import io.legado.app.ui.widget.components.list.ListScaffold
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
-import io.legado.app.ui.widget.components.tabRow.AdaptiveTabRow
+import io.legado.app.ui.widget.components.tabRow.AppTabRow
+import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.readText
 import io.legado.app.utils.startActivity
 import io.legado.app.utils.toastOnUi
@@ -237,37 +233,37 @@ fun BookshelfScreen(
         topBarActions = { },
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.add_remote_book)) },
+                text = stringResource(R.string.add_remote_book),
                 onClick = { context.startActivity<RemoteBookActivity>(); dismiss() },
                 leadingIcon = { Icon(Icons.Default.Wifi, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.book_local)) },
+                text = stringResource(R.string.book_local),
                 onClick = { context.startActivity<ImportBookActivity>(); dismiss() },
                 leadingIcon = { Icon(Icons.Default.Save, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.update_toc)) },
+                text = stringResource(R.string.update_toc),
                 onClick = { viewModel.upToc(uiState.items); dismiss() },
                 leadingIcon = { Icon(Icons.Default.Refresh, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText("布局设置") },
+                text = "布局设置",
                 onClick = { showConfigSheet = true; dismiss() },
                 leadingIcon = { Icon(Icons.Default.GridView, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.group_manage)) },
+                text = stringResource(R.string.group_manage),
                 onClick = { showGroupManageSheet = true; dismiss() },
                 leadingIcon = { Icon(Icons.Default.Edit, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.add_url)) },
+                text = stringResource(R.string.add_url),
                 onClick = { showAddUrlDialog = true; dismiss() },
                 leadingIcon = { Icon(Icons.Default.Link, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.bookshelf_management)) },
+                text = stringResource(R.string.bookshelf_management),
                 onClick = {
                     val groupId =
                         uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
@@ -279,7 +275,7 @@ fun BookshelfScreen(
                 leadingIcon = { Icon(Icons.Default.Settings, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.cache_export)) },
+                text = stringResource(R.string.cache_export),
                 onClick = {
                     val groupId =
                         uiState.groups.getOrNull(uiState.selectedGroupIndex)?.groupId ?: -1L
@@ -291,7 +287,7 @@ fun BookshelfScreen(
                 leadingIcon = { Icon(Icons.Default.Download, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.export_bookshelf)) },
+                text = stringResource(R.string.export_bookshelf),
                 onClick = {
                     showExportSheet = true
                     dismiss()
@@ -299,12 +295,12 @@ fun BookshelfScreen(
                 leadingIcon = { Icon(Icons.Default.ImportExport, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.import_bookshelf)) },
+                text = stringResource(R.string.import_bookshelf),
                 onClick = { showImportSheet = true; dismiss() },
                 leadingIcon = { Icon(Icons.Default.FileOpen, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.log)) },
+                text = stringResource(R.string.log),
                 onClick = {
                     showLogSheet = true
                     dismiss()
@@ -326,7 +322,7 @@ fun BookshelfScreen(
                             uiState.groups.map { it.groupName }
                         }
 
-                        AdaptiveTabRow(
+                        AppTabRow(
                             tabTitles = tabTitles,
                             selectedTabIndex = selectedTabIndex,
                             onTabSelected = { index ->
@@ -349,7 +345,7 @@ fun BookshelfScreen(
                                 ) { dismiss ->
                                     uiState.groups.forEachIndexed { index, group ->
                                         RoundDropdownMenuItem(
-                                            text = { AppText(group.groupName) },
+                                            text = group.groupName,
                                             onClick = {
                                                 scope.launch { pagerState.animateScrollToPage(index) }
                                                 dismiss()
@@ -487,13 +483,15 @@ fun BookshelfScreen(
         }
     }
 
-    if (showConfigSheet) {
-        BookshelfConfigSheet(onDismissRequest = { showConfigSheet = false })
-    }
+    BookshelfConfigSheet(
+        show = showConfigSheet,
+        onDismissRequest = { showConfigSheet = false }
+    )
 
-    if (showGroupManageSheet) {
-        GroupManageSheet(onDismissRequest = { showGroupManageSheet = false })
-    }
+    GroupManageSheet(
+        show = showGroupManageSheet,
+        onDismissRequest = { showGroupManageSheet = false }
+    )
 
     if (showAddUrlDialog) {
         SourceInputDialog(
@@ -506,40 +504,39 @@ fun BookshelfScreen(
         )
     }
 
-    if (showImportSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showImportSheet = false },
-            title = stringResource(R.string.import_bookshelf),
-            onSelectSysFile = { types ->
-                importLauncher.launch(types)
-                showImportSheet = false
-            },
-            onManualInput = {
-                showAddUrlDialog = true
-                showImportSheet = false
-            },
-            allowExtensions = arrayOf("json", "txt")
-        )
-    }
+    FilePickerSheet(
+        show = showImportSheet,
+        onDismissRequest = { showImportSheet = false },
+        title = stringResource(R.string.import_bookshelf),
+        onSelectSysFile = { types ->
+            importLauncher.launch(types)
+            showImportSheet = false
+        },
+        onManualInput = {
+            showAddUrlDialog = true
+            showImportSheet = false
+        },
+        allowExtensions = arrayOf("json", "txt")
+    )
 
-    if (showExportSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showExportSheet = false },
-            title = stringResource(R.string.export_bookshelf),
-            onSelectSysDir = {
-                showExportSheet = false
-                exportLauncher.launch("bookshelf.json")
-            },
-            onUpload = {
-                showExportSheet = false
-                viewModel.uploadBookshelf(uiState.items)
-            }
-        )
-    }
+    FilePickerSheet(
+        show = showExportSheet,
+        onDismissRequest = { showExportSheet = false },
+        title = stringResource(R.string.export_bookshelf),
+        onSelectSysDir = {
+            showExportSheet = false
+            exportLauncher.launch("bookshelf.json")
+        },
+        onUpload = {
+            showExportSheet = false
+            viewModel.uploadBookshelf(uiState.items)
+        }
+    )
 
-    if (showLogSheet) {
-        AppLogSheet(onDismissRequest = { showLogSheet = false })
-    }
+    AppLogSheet(
+        show = showLogSheet,
+        onDismissRequest = { showLogSheet = false }
+    )
 
     if (uiState.isLoading) {
         Dialog(onDismissRequest = {}) {

+ 39 - 58
app/src/main/java/io/legado/app/ui/main/bookshelf/GroupManageSheet.kt

@@ -6,18 +6,13 @@ import androidx.compose.animation.fadeIn
 import androidx.compose.animation.fadeOut
 import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.lazy.rememberLazyListState
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -26,7 +21,6 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.res.stringResource
@@ -35,10 +29,9 @@ import io.legado.app.R
 import io.legado.app.data.entities.BookGroup
 import io.legado.app.ui.book.group.GroupEditContent
 import io.legado.app.ui.book.group.GroupViewModel
-import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.widget.components.button.SmallIconButton
 import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
-import io.legado.app.ui.widget.components.text.AppText
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.utils.move
 import org.koin.androidx.compose.koinViewModel
 import sh.calvin.reorderable.rememberReorderableLazyListState
@@ -46,6 +39,7 @@ import sh.calvin.reorderable.rememberReorderableLazyListState
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun GroupManageSheet(
+    show: Boolean,
     onDismissRequest: () -> Unit,
     viewModel: GroupViewModel = koinViewModel(),
     bookshelfViewModel: BookshelfViewModel = koinViewModel()
@@ -78,7 +72,22 @@ fun GroupManageSheet(
         }
     }
 
-    GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
+    AppModalBottomSheet(
+        show = show,
+        onDismissRequest = onDismissRequest,
+        title = if (!isEditing) stringResource(R.string.group_manage) else stringResource(R.string.group_edit),
+        endAction = {
+            if (!isEditing) {
+                SmallIconButton(
+                    onClick = {
+                        editingGroup = null
+                        isEditing = true
+                    },
+                    icon = Icons.Default.Add
+                )
+            }
+        }
+    ) {
         AnimatedContent(
             targetState = isEditing,
             transitionSpec = {
@@ -93,56 +102,28 @@ fun GroupManageSheet(
                     viewModel = viewModel
                 )
             } else {
-                Column(
-                    modifier = Modifier
-                        .fillMaxWidth()
+                LazyColumn(
+                    state = listState,
+                    modifier = Modifier.fillMaxWidth(),
+                    verticalArrangement = Arrangement.spacedBy(8.dp)
                 ) {
-                    Row(
-                        modifier = Modifier
-                            .fillMaxWidth()
-                            .padding(horizontal = 16.dp)
-                            .padding(vertical = 8.dp),
-                        horizontalArrangement = Arrangement.SpaceBetween,
-                        verticalAlignment = Alignment.CenterVertically
-                    ) {
-                        AppText(
-                            text = stringResource(R.string.group_manage),
-                            style = LegadoTheme.typography.titleLarge
+                    items(listData, key = { it.groupId }) { group ->
+                        val manageNameInfo = remember(group) { group.getManageName(context) }
+                        ReorderableSelectionItem(
+                            state = reorderableState,
+                            key = group.groupId,
+                            title = group.groupName.ifBlank { manageNameInfo.suffix.orEmpty() },
+                            subtitle = if (group.groupName.isNotBlank()) manageNameInfo.suffix else null,
+                            isEnabled = group.show,
+                            containerColor = MaterialTheme.colorScheme.surface,
+                            onEnabledChange = { isChecked ->
+                                viewModel.upGroup(group.copy(show = isChecked))
+                            },
+                            onClickEdit = {
+                                editingGroup = group
+                                isEditing = true
+                            }
                         )
-                        IconButton(onClick = {
-                            editingGroup = null
-                            isEditing = true
-                        }) {
-                            Icon(
-                                Icons.Default.Add,
-                                contentDescription = stringResource(R.string.add)
-                            )
-                        }
-                    }
-
-                    LazyColumn(
-                        state = listState,
-                        modifier = Modifier.fillMaxWidth(),
-                        verticalArrangement = Arrangement.spacedBy(8.dp)
-                    ) {
-                        items(listData, key = { it.groupId }) { group ->
-                            val manageNameInfo = remember(group) { group.getManageName(context) }
-                            ReorderableSelectionItem(
-                                state = reorderableState,
-                                key = group.groupId,
-                                title = group.groupName.ifBlank { manageNameInfo.suffix.orEmpty() },
-                                subtitle = if (group.groupName.isNotBlank()) manageNameInfo.suffix else null,
-                                isEnabled = group.show,
-                                containerColor = MaterialTheme.colorScheme.surface,
-                                onEnabledChange = { isChecked ->
-                                    viewModel.upGroup(group.copy(show = isChecked))
-                                },
-                                onClickEdit = {
-                                    editingGroup = group
-                                    isEditing = true
-                                }
-                            )
-                        }
                     }
                 }
             }

+ 9 - 13
app/src/main/java/io/legado/app/ui/main/explore/ExploreScreen.kt

@@ -153,13 +153,13 @@ fun ExploreScreen(
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
                 leadingIcon = { MenuItemIcon(Icons.Default.Group) },
-                text = { AppText(stringResource(R.string.all)) },
+                text = stringResource(R.string.all),
                 onClick = { viewModel.setGroup(""); dismiss() }
             )
             uiState.groups.forEach { group ->
                 RoundDropdownMenuItem(
                     leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Outlined.Label) },
-                    text = { AppText(group) },
+                    text = group,
                     onClick = { viewModel.setGroup(group); dismiss() }
                 )
             }
@@ -416,29 +416,29 @@ fun ExploreSourceHeader(
                     PillHeaderDivider(title = item.bookSourceName)
                     RoundDropdownMenuItem(
                         leadingIcon = { MenuItemIcon(Icons.Default.VerticalAlignTop) },
-                        text = { AppText(stringResource(R.string.to_top)) },
+                        text = stringResource(R.string.to_top),
                         onClick = { onTop(); showMenu = false }
                     )
                     RoundDropdownMenuItem(
                         leadingIcon = { MenuItemIcon(Icons.Default.Edit) },
-                        text = { AppText(stringResource(R.string.edit)) },
+                        text = stringResource(R.string.edit),
                         onClick = { onEdit(); showMenu = false }
                     )
                     RoundDropdownMenuItem(
                         leadingIcon = { MenuItemIcon(Icons.Default.Search) },
-                        text = { AppText(stringResource(R.string.search)) },
+                        text = stringResource(R.string.search),
                         onClick = { onSearch(); showMenu = false }
                     )
                     if (item.hasLoginUrl) {
                         RoundDropdownMenuItem(
                             leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Filled.Login) },
-                            text = { AppText(stringResource(R.string.login)) },
+                            text = stringResource(R.string.login),
                             onClick = { onLogin(); showMenu = false }
                         )
                     }
                     RoundDropdownMenuItem(
                         leadingIcon = { MenuItemIcon(Icons.Default.Refresh) },
-                        text = { AppText(stringResource(R.string.refresh)) },
+                        text = stringResource(R.string.refresh),
                         onClick = { onRefresh(); showMenu = false }
                     )
                     RoundDropdownMenuItem(
@@ -448,12 +448,8 @@ fun ExploreSourceHeader(
                                 tint = MaterialTheme.colorScheme.error
                             )
                         },
-                        text = {
-                            AppText(
-                                stringResource(R.string.delete),
-                                color = MaterialTheme.colorScheme.error
-                            )
-                        },
+                        text = stringResource(R.string.delete),
+                        color = LegadoTheme.colorScheme.error,
                         onClick = { onDelete(); showMenu = false }
                     )
                 }

+ 1 - 1
app/src/main/java/io/legado/app/ui/main/my/MyScreen.kt

@@ -239,7 +239,7 @@ fun WebServiceSettingBlock(
             Row(
                 modifier = Modifier
                     .fillMaxWidth()
-                    .padding(start = 16.dp, end = 16.dp, bottom = 12.dp),
+                    .padding(start = 16.dp, end = 16.dp, top = 12.dp, bottom = 12.dp),
                 horizontalArrangement = Arrangement.End
             ) {
                 SmallTextButton(

+ 9 - 13
app/src/main/java/io/legado/app/ui/main/rss/RssScreen.kt

@@ -136,12 +136,12 @@ fun RssScreen(
                     dismiss()
                 },
                 leadingIcon = { MenuItemIcon(Icons.Default.Settings) },
-                text = { AppText("订阅源管理") }
+                text = "订阅源管理"
             )
             PillDivider()
             RoundDropdownMenuItem(
                 leadingIcon = { MenuItemIcon(Icons.Default.Group) },
-                text = { AppText(stringResource(R.string.all)) },
+                text = stringResource(R.string.all),
                 onClick = {
                     viewModel.setGroup("")
                     dismiss()
@@ -150,7 +150,7 @@ fun RssScreen(
             uiState.groups.forEach { group ->
                 RoundDropdownMenuItem(
                     leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Outlined.Label) },
-                    text = { AppText(group) },
+                    text = group,
                     onClick = {
                         viewModel.setGroup(group)
                         dismiss()
@@ -247,7 +247,7 @@ fun RssSourceGridItem(
                 PillHeaderDivider(title = source.sourceName)
                 RoundDropdownMenuItem(
                     leadingIcon = { MenuItemIcon(Icons.Default.VerticalAlignTop) },
-                    text = { AppText(stringResource(R.string.to_top)) },
+                    text = stringResource(R.string.to_top),
                     onClick = {
                         onTop()
                         showMenu = false
@@ -255,7 +255,7 @@ fun RssSourceGridItem(
                 )
                 RoundDropdownMenuItem(
                     leadingIcon = { MenuItemIcon(Icons.Default.Edit) },
-                    text = { AppText(stringResource(R.string.edit)) },
+                    text = stringResource(R.string.edit),
                     onClick = {
                         onEdit()
                         showMenu = false
@@ -264,7 +264,7 @@ fun RssSourceGridItem(
                 if (!source.loginUrl.isNullOrBlank()) {
                     RoundDropdownMenuItem(
                         leadingIcon = { MenuItemIcon(Icons.AutoMirrored.Filled.Login) },
-                        text = { AppText(stringResource(R.string.login)) },
+                        text = stringResource(R.string.login),
                         onClick = {
                             onLogin()
                             showMenu = false
@@ -273,7 +273,7 @@ fun RssSourceGridItem(
                 }
                 RoundDropdownMenuItem(
                     leadingIcon = { MenuItemIcon(Icons.Default.Close) },
-                    text = { AppText(stringResource(R.string.disable_source)) },
+                    text = stringResource(R.string.disable_source),
                     onClick = {
                         onDisable()
                         showMenu = false
@@ -286,12 +286,8 @@ fun RssSourceGridItem(
                             tint = MaterialTheme.colorScheme.error
                         )
                     },
-                    text = {
-                        AppText(
-                            stringResource(R.string.delete),
-                            color = MaterialTheme.colorScheme.error
-                        )
-                    },
+                    text = stringResource(R.string.delete),
+                    color = MaterialTheme.colorScheme.error,
                     onClick = {
                         onDelete()
                         showMenu = false

+ 64 - 74
app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt

@@ -8,7 +8,6 @@ import androidx.compose.animation.SharedTransitionScope
 import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
@@ -19,7 +18,6 @@ import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.FileOpen
 import androidx.compose.material3.AlertDialog
-import androidx.compose.material3.DropdownMenuItem
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.FloatingActionButton
@@ -57,7 +55,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import io.legado.app.R
 import io.legado.app.base.BaseRuleEvent
 import io.legado.app.data.entities.ReplaceRule
-import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.DraggableSelectionHandler
 import io.legado.app.ui.widget.components.GroupManageBottomSheet
@@ -70,7 +67,7 @@ import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
 import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.ui.widget.components.rules.RuleListScaffold
-import io.legado.app.ui.widget.components.tabRow.AdaptiveTabRow
+import io.legado.app.ui.widget.components.tabRow.AppTabRow
 import io.legado.app.ui.widget.components.text.AppText
 import kotlinx.coroutines.launch
 import org.koin.androidx.compose.koinViewModel
@@ -161,56 +158,48 @@ fun ReplaceRuleScreen(
         )
     }
 
-    if (showImportSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showImportSheet = false },
-            title = stringResource(R.string.import_replace_rule),
-            onSelectSysFile = { types ->
-                importDoc.launch(types)
-                showImportSheet = false
-            },
-            onManualInput = {
-                showUrlInput = true
-                showImportSheet = false
-            },
-            allowExtensions = arrayOf("json", "txt")
-        )
-    }
+    FilePickerSheet(
+        show = showImportSheet,
+        onDismissRequest = { showImportSheet = false },
+        title = stringResource(R.string.import_replace_rule),
+        onSelectSysFile = { types ->
+            importDoc.launch(types)
+            showImportSheet = false
+        },
+        onManualInput = {
+            showUrlInput = true
+            showImportSheet = false
+        },
+        allowExtensions = arrayOf("json", "txt")
+    )
 
-    if (showExportSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showExportSheet = false },
-            onSelectSysDir = {
-                showExportSheet = false
-                exportDoc.launch("exportReplaceRule.json")
-            },
-            onUpload = {
-                showExportSheet = false
-                viewModel.uploadSelectedRules(selectedIds, rules)
-            },
-            allowExtensions = arrayOf("json")
-        )
-    }
+    FilePickerSheet(
+        show = showExportSheet,
+        onDismissRequest = { showExportSheet = false },
+        onSelectSysDir = {
+            showExportSheet = false
+            exportDoc.launch("exportReplaceRule.json")
+        },
+        onUpload = {
+            showExportSheet = false
+            viewModel.uploadSelectedRules(selectedIds, rules)
+        },
+        allowExtensions = arrayOf("json")
+    )
 
-    (importState as? BaseImportUiState.Success<ReplaceRule>)?.let { state ->
-        BatchImportDialog(
-            title = stringResource(R.string.import_replace_rule),
-            importState = state,
-            onDismissRequest = { viewModel.cancelImport() },
-            onToggleItem = { viewModel.toggleImportSelection(it) },
-            onToggleAll = { viewModel.toggleImportAll(it) },
-            onConfirm = { viewModel.saveImportedRules() },
-            topBarActions = {},
-            itemContent = { rule, _ ->
-                Column {
-                    AppText(rule.name, style = LegadoTheme.typography.titleMedium)
-                    if (!rule.group.isNullOrBlank()) {
-                        AppText(rule.group!!, style = LegadoTheme.typography.bodySmall)
-                    }
-                }
-            }
-        )
-    }
+    BatchImportDialog(
+        title = stringResource(R.string.import_replace_rule),
+        importState = importState,
+        onDismissRequest = { viewModel.cancelImport() },
+        onToggleItem = { viewModel.toggleImportSelection(it) },
+        onToggleAll = { viewModel.toggleImportAll(it) },
+        onConfirm = { viewModel.saveImportedRules() },
+        topBarActions = {},
+        itemTitle = { rule -> rule.name },
+        itemSubtitle = { rule ->
+            rule.group?.takeIf { it.isNotBlank() }
+        }
+    )
 
     if (importState is BaseImportUiState.Loading) {
         Dialog(onDismissRequest = { viewModel.cancelImport() }) { LoadingIndicator() }
@@ -263,14 +252,15 @@ fun ReplaceRuleScreen(
         }
     }
 
-    if (showGroupManageSheet) {
-        GroupManageBottomSheet(
-            groups = groups,
-            onDismissRequest = { showGroupManageSheet = false },
-            onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
-            onDeleteGroup = { viewModel.delGroup(it) }
-        )
-    }
+
+    GroupManageBottomSheet(
+        show = showGroupManageSheet,
+        groups = groups,
+        onDismissRequest = { showGroupManageSheet = false },
+        onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
+        onDeleteGroup = { viewModel.delGroup(it) }
+    )
+
 
     showDeleteRuleDialog?.let { rule ->
         AlertDialog(
@@ -343,7 +333,7 @@ fun ReplaceRuleScreen(
         },
         bottomContent = {
             if (tabItems.size > 1) {
-                AdaptiveTabRow(
+                AppTabRow(
                     tabTitles = tabItems,
                     selectedTabIndex = selectedTabIndex,
                     onTabSelected = { index ->
@@ -384,29 +374,29 @@ fun ReplaceRuleScreen(
         snackbarHostState = snackbarHostState,
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.import_str)) },
+                text = stringResource(R.string.import_str),
                 onClick = { showImportSheet = true; dismiss() },
                 leadingIcon = { Icon(Icons.Default.FileOpen, null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText("分组管理") },
+                text = "分组管理",
                 onClick = { showGroupManageSheet = true; dismiss() }
             )
             RoundDropdownMenuItem(
-                text = { AppText("帮助") },
+                text = "帮助",
                 onClick = { /*TODO*/ dismiss() }
             )
             PillDivider()
             RoundDropdownMenuItem(
-                text = { AppText("旧的在前") },
+                text = "旧的在前",
                 onClick = { viewModel.setSortMode("asc"); dismiss() }
             )
             RoundDropdownMenuItem(
-                text = { AppText("新的在前") },
+                text = "新的在前",
                 onClick = { viewModel.setSortMode("desc"); dismiss() }
             )
             RoundDropdownMenuItem(
-                text = { AppText("名称升序") },
+                text = "名称升序",
                 onClick = {
                     viewModel.setSortMode("name_asc")
                     dismiss()
@@ -416,7 +406,7 @@ fun ReplaceRuleScreen(
                 }
             )
             RoundDropdownMenuItem(
-                text = { AppText("名称降序") },
+                text = "名称降序",
                 onClick = {
                     viewModel.setSortMode("name_desc")
                     dismiss()
@@ -473,16 +463,16 @@ fun ReplaceRuleScreen(
                                     //clipInOverlayDuringTransition = OverlayClip(RoundedCornerShape(12.dp))
                                 ),
                             dropdownContent = { dismiss ->
-                                DropdownMenuItem(
-                                    text = { AppText("移至顶部") },
+                                RoundDropdownMenuItem(
+                                    text = "移至顶部",
                                     onClick = { viewModel.toTop(ui.rule); dismiss() }
                                 )
-                                DropdownMenuItem(
-                                    text = { AppText("移至底部") },
+                                RoundDropdownMenuItem(
+                                    text = "移至底部",
                                     onClick = { viewModel.toBottom(ui.rule); dismiss() }
                                 )
-                                DropdownMenuItem(
-                                    text = { AppText("删除") },
+                                RoundDropdownMenuItem(
+                                    text = "删除",
                                     onClick = { showDeleteRuleDialog = ui.rule; dismiss() }
                                 )
                             }

+ 10 - 17
app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesScreen.kt

@@ -3,11 +3,9 @@ package io.legado.app.ui.rss.favorites
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.material.icons.Icons
@@ -44,7 +42,6 @@ import io.legado.app.ui.widget.components.dialog.TextListInputDialog
 import io.legado.app.ui.widget.components.divider.PillDivider
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.ui.widget.components.rules.RuleListScaffold
-import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.utils.startActivity
 
 @OptIn(ExperimentalMaterial3Api::class)
@@ -129,11 +126,10 @@ fun RssFavoritesScreen(
         topBarActions = {},
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
-                text = {
+                text = stringResource(R.string.all),
+                leadingIcon = {
                     Row(verticalAlignment = Alignment.CenterVertically) {
                         Icon(Icons.Default.Group, null, modifier = Modifier.size(18.dp))
-                        Spacer(Modifier.width(12.dp))
-                        AppText(stringResource(R.string.all))
                     }
                 },
                 onClick = {
@@ -143,15 +139,14 @@ fun RssFavoritesScreen(
             )
             groups.forEach { group ->
                 RoundDropdownMenuItem(
-                    text = {
+                    text = group,
+                    leadingIcon = {
                         Row(verticalAlignment = Alignment.CenterVertically) {
                             Icon(
                                 Icons.AutoMirrored.Outlined.Label,
                                 null,
                                 modifier = Modifier.size(18.dp)
                             )
-                            Spacer(Modifier.width(12.dp))
-                            AppText(group)
                         }
                     },
                     onClick = {
@@ -162,11 +157,10 @@ fun RssFavoritesScreen(
             }
             PillDivider()
             RoundDropdownMenuItem(
-                text = {
+                text = stringResource(R.string.delete_select_group),
+                leadingIcon = {
                     Row(verticalAlignment = Alignment.CenterVertically) {
                         Icon(Icons.Default.DeleteSweep, null, modifier = Modifier.size(18.dp))
-                        Spacer(Modifier.width(12.dp))
-                        AppText(stringResource(R.string.delete_select_group))
                     }
                 },
                 onClick = {
@@ -175,11 +169,10 @@ fun RssFavoritesScreen(
                 }
             )
             RoundDropdownMenuItem(
-                text = {
+                text = stringResource(R.string.all),
+                leadingIcon = {
                     Row(verticalAlignment = Alignment.CenterVertically) {
                         Icon(Icons.Default.DeleteForever, null, modifier = Modifier.size(18.dp))
-                        Spacer(Modifier.width(12.dp))
-                        AppText(stringResource(R.string.all))
                     }
                 },
                 onClick = {
@@ -248,14 +241,14 @@ fun RssFavoritesScreen(
                         },
                         dropdownContent = { dismiss ->
                             RoundDropdownMenuItem(
-                                text = { AppText(stringResource(R.string.change_group)) },
+                                text = stringResource(R.string.change_group),
                                 onClick = {
                                     showSetGroupDialog = rssStar
                                     dismiss()
                                 }
                             )
                             RoundDropdownMenuItem(
-                                text = { AppText(stringResource(R.string.delete)) },
+                                text = stringResource(R.string.delete),
                                 onClick = {
                                     viewModel.deleteStar(rssStar)
                                     dismiss()

+ 44 - 56
app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceScreen.kt

@@ -5,7 +5,6 @@ import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.result.contract.ActivityResultContracts
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxSize
@@ -41,7 +40,6 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import io.legado.app.R
 import io.legado.app.base.BaseRuleEvent
 import io.legado.app.data.entities.RssSource
-import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.ActionItem
 import io.legado.app.ui.widget.components.DraggableSelectionHandler
 import io.legado.app.ui.widget.components.GroupManageBottomSheet
@@ -50,7 +48,6 @@ import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
 import io.legado.app.ui.widget.components.dialog.TextListInputDialog
 import io.legado.app.ui.widget.components.divider.PillDivider
 import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
-import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
 import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
 import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
 import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
@@ -175,50 +172,41 @@ fun RssSourceScreen(
         )
     }
 
-    if (showGroupManageSheet) {
-        GroupManageBottomSheet(
-            groups = groups,
-            onDismissRequest = { showGroupManageSheet = false },
-            onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
-            onDeleteGroup = { viewModel.delGroup(it) }
-        )
-    }
+    GroupManageBottomSheet(
+        show = showGroupManageSheet,
+        groups = groups,
+        onDismissRequest = { showGroupManageSheet = false },
+        onUpdateGroup = { old, new -> viewModel.upGroup(old, new) },
+        onDeleteGroup = { viewModel.delGroup(it) }
+    )
 
-    if (showFilePickerSheet) {
-        FilePickerSheet(
-            onDismissRequest = { showFilePickerSheet = false },
-            onSelectSysDir = {
-                showFilePickerSheet = false
-                exportDoc.launch("exportRssSource.json")
-            },
-            onUpload = {
-                showFilePickerSheet = false
-                viewModel.uploadSelectedRules(selectedIds, rules)
-            },
-            allowExtensions = arrayOf("json")
-        )
-    }
 
-    (importState as? BaseImportUiState.Success<RssSource>)?.let { state ->
-        BatchImportDialog(
-            title = stringResource(R.string.import_rss_source),
-            importState = state,
-            onDismissRequest = { viewModel.cancelImport() },
-            onToggleItem = { viewModel.toggleImportSelection(it) },
-            onToggleAll = { viewModel.toggleImportAll(it) },
-            onConfirm = { viewModel.saveImportedRules() },
-            itemContent = { source, _ ->
-                Column {
-                    AppText(source.sourceName, style = LegadoTheme.typography.titleMedium)
-                    AppText(
-                        source.sourceUrl,
-                        style = LegadoTheme.typography.bodySmall,
-                        maxLines = 1
-                    )
-                }
-            }
-        )
-    }
+    FilePickerSheet(
+        show = showFilePickerSheet,
+        onDismissRequest = { showFilePickerSheet = false },
+        onSelectSysDir = {
+            showFilePickerSheet = false
+            exportDoc.launch("exportRssSource.json")
+        },
+        onUpload = {
+            showFilePickerSheet = false
+            viewModel.uploadSelectedRules(selectedIds, rules)
+        },
+        allowExtensions = arrayOf("json")
+    )
+
+    BatchImportDialog(
+        title = stringResource(R.string.import_rss_source),
+        importState = importState,
+        onDismissRequest = { viewModel.cancelImport() },
+        onToggleItem = { viewModel.toggleImportSelection(it) },
+        onToggleAll = { viewModel.toggleImportAll(it) },
+        onConfirm = { viewModel.saveImportedRules() },
+        itemTitle = { rule -> rule.sourceName },
+        itemSubtitle = { rule ->
+            rule.sourceUrl.takeIf { it.isNotBlank() }
+        }
+    )
 
     LaunchedEffect(reorderableState.isAnyItemDragging) {
         if (!reorderableState.isAnyItemDragging) {
@@ -291,11 +279,11 @@ fun RssSourceScreen(
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
                 onClick = { showGroupManageSheet = true },
-                text = { AppText("分组管理") },
+                text = "分组管理",
             )
             Box {
                 RoundDropdownMenuItem(
-                    text = { AppText(stringResource(R.string.import_rss_source)) },
+                    text = stringResource(R.string.import_rss_source),
                     onClick = { showImportMenu = true }
                 )
                 RoundDropdownMenu(
@@ -303,7 +291,7 @@ fun RssSourceScreen(
                     onDismissRequest = { showImportMenu = false }
                 ) {
                     RoundDropdownMenuItem(
-                        text = { AppText(stringResource(R.string.import_on_line)) },
+                        text = stringResource(R.string.import_on_line),
                         onClick = {
                             showImportMenu = false
                             dismiss()
@@ -311,7 +299,7 @@ fun RssSourceScreen(
                         }
                     )
                     RoundDropdownMenuItem(
-                        text = { AppText(stringResource(R.string.import_local)) },
+                        text = stringResource(R.string.import_local),
                         onClick = {
                             showImportMenu = false
                             dismiss()
@@ -319,7 +307,7 @@ fun RssSourceScreen(
                         }
                     )
                     RoundDropdownMenuItem(
-                        text = { AppText(stringResource(R.string.import_default_rule)) },
+                        text = stringResource(R.string.import_default_rule),
                         onClick = {
                             showImportMenu = false
                             dismiss()
@@ -330,29 +318,29 @@ fun RssSourceScreen(
             }
             PillDivider()
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.all)) },
+                text = stringResource(R.string.all),
                 onClick = { dismiss(); viewModel.setGroupFilter(null) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.enabled)) },
+                text = stringResource(R.string.enabled),
                 onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_ENABLED) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.disabled)) },
+                text = stringResource(R.string.disabled),
                 onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_DISABLED) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.need_login)) },
+                text = stringResource(R.string.need_login),
                 onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_LOGIN) }
             )
             RoundDropdownMenuItem(
-                text = { AppText(stringResource(R.string.no_group)) },
+                text = stringResource(R.string.no_group),
                 onClick = { dismiss(); viewModel.setGroupFilter(RssSourceViewModel.FILTER_NO_GROUP) }
             )
             PillDivider()
             groups.forEach { group ->
                 RoundDropdownMenuItem(
-                    text = { AppText(group) },
+                    text = group,
                     onClick = { dismiss(); viewModel.setGroupFilter("${RssSourceViewModel.PREFIX_GROUP}$group") }
                 )
             }

+ 3 - 2
app/src/main/java/io/legado/app/ui/rss/subscription/RuleSubScreen.kt

@@ -89,7 +89,8 @@ fun RuleSubScreen(
         topBarActions = {},
         dropDownMenuContent = { dismiss ->
             RoundDropdownMenuItem(
-                text = {
+                text = stringResource(R.string.sort),
+                leadingIcon = {
                     Row(verticalAlignment = Alignment.CenterVertically) {
                         Icon(Icons.AutoMirrored.Filled.Sort, null, modifier = Modifier.size(18.dp))
                         Spacer(Modifier.size(12.dp))
@@ -185,7 +186,7 @@ fun RuleSubScreen(
                         },
                         dropdownContent = { dismiss ->
                             RoundDropdownMenuItem(
-                                text = { AppText(stringResource(R.string.delete)) },
+                                text = stringResource(R.string.delete),
                                 onClick = {
                                     viewModel.delete(ruleSub)
                                     dismiss()

+ 225 - 32
app/src/main/java/io/legado/app/ui/theme/AppTheme.kt

@@ -11,6 +11,7 @@ import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.remember
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.font.FontWeight
 import io.legado.app.ui.config.themeConfig.ThemeConfig
 import top.yukonga.miuix.kmp.theme.MiuixTheme
 import top.yukonga.miuix.kmp.theme.ThemeController
@@ -46,8 +47,15 @@ fun AppTheme(
             )
         }
 
-    val themeColors = remember(colorScheme, darkTheme, seedColor, paletteStyle, colorSchemeMode) {
-        LegadoThemeColors(
+    val themeColors = remember(
+        colorScheme,
+        darkTheme,
+        seedColor,
+        paletteStyle,
+        colorSchemeMode,
+        composeEngine
+    ) {
+        LegadoThemeMode(
             colorScheme = colorScheme,
             isDark = darkTheme,
             seedColor = seedColor,
@@ -61,45 +69,230 @@ fun AppTheme(
     CompositionLocalProvider(
         LocalLegadoThemeColors provides themeColors
     ) {
-        when {
-            ThemeResolver.isMiuixEngine(themeColors.composeEngine) -> {
-                val controller = remember(colorSchemeMode, darkTheme) {
-                    ThemeController(
-                        colorSchemeMode = colorSchemeMode,
-                        isDark = darkTheme
+        if (ThemeResolver.isMiuixEngine(themeColors.composeEngine)) {
+            val controller = remember(colorSchemeMode, darkTheme) {
+                ThemeController(colorSchemeMode = colorSchemeMode, isDark = darkTheme)
+            }
+
+            MiuixTheme(controller = controller) {
+                val miuixStyles = MiuixTheme.textStyles
+                val legadoTypography = remember(miuixStyles) {
+                    LegadoTypography(
+                        headlineLarge = miuixStyles.title1,
+                        headlineLargeEmphasized = miuixStyles.title1.copy(fontWeight = FontWeight.Medium),
+                        headlineMedium = miuixStyles.title2,
+                        headlineMediumEmphasized = miuixStyles.title2.copy(fontWeight = FontWeight.Medium),
+                        headlineSmall = miuixStyles.title3,
+                        headlineSmallEmphasized = miuixStyles.title3.copy(fontWeight = FontWeight.Medium),
+
+
+                        titleLarge = miuixStyles.headline1,
+                        titleLargeEmphasized = miuixStyles.headline1.copy(fontWeight = FontWeight.Medium),
+                        titleMedium = miuixStyles.headline2,
+                        titleMediumEmphasized = miuixStyles.headline2.copy(fontWeight = FontWeight.Medium),
+                        titleSmall = miuixStyles.subtitle,
+                        titleSmallEmphasized = miuixStyles.subtitle.copy(fontWeight = FontWeight.Medium),
+
+
+                        bodyLarge = miuixStyles.paragraph,
+                        bodyLargeEmphasized = miuixStyles.paragraph.copy(fontWeight = FontWeight.Medium),
+                        bodyMedium = miuixStyles.body1,
+                        bodyMediumEmphasized = miuixStyles.body1.copy(fontWeight = FontWeight.Medium),
+                        bodySmall = miuixStyles.body2,
+                        bodySmallEmphasized = miuixStyles.body2.copy(fontWeight = FontWeight.Medium),
+
+                        labelLarge = miuixStyles.button,
+                        labelLargeEmphasized = miuixStyles.button.copy(fontWeight = FontWeight.Medium),
+                        labelMedium = miuixStyles.footnote1,
+                        labelMediumEmphasized = miuixStyles.footnote1.copy(fontWeight = FontWeight.Medium),
+                        labelSmall = miuixStyles.footnote2,
+                        labelSmallEmphasized = miuixStyles.footnote2.copy(fontWeight = FontWeight.Medium)
+                    )
+                }
+
+                val miuixColorScheme = MiuixTheme.colorScheme
+
+                val mappedColorScheme = remember(miuixColorScheme) {
+                    LegadoColorScheme(
+                        primary = miuixColorScheme.primary,
+                        onPrimary = miuixColorScheme.onPrimary,
+                        primaryContainer = miuixColorScheme.primaryContainer,
+                        onPrimaryContainer = miuixColorScheme.onPrimaryContainer,
+                        inversePrimary = miuixColorScheme.primaryVariant,
+
+                        secondary = miuixColorScheme.primary.copy(alpha = 0.5f),
+                        onSecondary = miuixColorScheme.onPrimary.copy(alpha = 0.5f),
+                        secondaryContainer = miuixColorScheme.primaryContainer.copy(alpha = 0.5f),
+                        onSecondaryContainer = miuixColorScheme.onPrimaryContainer.copy(alpha = 0.5f),
+
+                        tertiary = miuixColorScheme.secondary,
+                        onTertiary = miuixColorScheme.onSecondary,
+                        tertiaryContainer = miuixColorScheme.secondaryContainer,
+                        onTertiaryContainer = miuixColorScheme.onSecondaryContainer,
+
+                        // ================= 4. 背景与表面 (Background & Surface) =================
+                        background = miuixColorScheme.background,
+                        onBackground = miuixColorScheme.onBackground,
+
+                        surface = miuixColorScheme.surface,
+                        onSurface = miuixColorScheme.onSurface,
+                        surfaceVariant = miuixColorScheme.surfaceVariant,
+                        // M3 的 onSurfaceVariant 通常是次级文字色。Miuix 的 onSurfaceSecondary 完美契合这个语义
+                        onSurfaceVariant = miuixColorScheme.onSurfaceSecondary,
+
+                        // M3 中用于给 Surface 叠加一层极淡主题色的属性,通常直接取 primary
+                        surfaceTint = miuixColorScheme.primary,
+
+                        // Inverse 系列通常用于深色模式下的反色提示(如 Snackbar)。
+                        // 简单映射法:直接用现有的 onSurface 和 surface 交叉互换。
+                        inverseSurface = miuixColorScheme.onSurface,
+                        inverseOnSurface = miuixColorScheme.surface,
+
+                        // ================= 5. 错误状态 (Error) =================
+                        error = miuixColorScheme.error,
+                        onError = miuixColorScheme.onError,
+                        errorContainer = miuixColorScheme.errorContainer,
+                        onErrorContainer = miuixColorScheme.onErrorContainer,
+
+                        // ================= 6. 边框、分割线与遮罩 (Outline & Scrim) =================
+                        outline = miuixColorScheme.outline,
+                        // outlineVariant 在 M3 中常用于分割线。Miuix 刚好有 dividerLine
+                        outlineVariant = miuixColorScheme.dividerLine,
+                        // scrim 是 M3 的遮罩层(如弹窗背后的阴影)。Miuix 刚好有 windowDimming
+                        scrim = miuixColorScheme.windowDimming,
+
+                        surfaceBright = miuixColorScheme.surface, // Miuix 缺省,用 surface 兜底
+                        surfaceDim = miuixColorScheme.background, // Miuix 缺省,用 background 兜底
+                        surfaceContainer = miuixColorScheme.surfaceContainer,
+                        surfaceContainerHigh = miuixColorScheme.surfaceContainerHigh,
+                        surfaceContainerHighest = miuixColorScheme.surfaceContainerHighest,
+                        surfaceContainerLow = miuixColorScheme.secondaryContainer,
+                        surfaceContainerLowest = miuixColorScheme.background,
+
+                        primaryFixed = miuixColorScheme.primaryContainer,
+                        primaryFixedDim = miuixColorScheme.primary,
+                        onPrimaryFixed = miuixColorScheme.onPrimaryContainer,
+                        onPrimaryFixedVariant = miuixColorScheme.onPrimary,
+                        secondaryFixed = miuixColorScheme.secondaryContainer,
+                        secondaryFixedDim = miuixColorScheme.secondary,
+                        onSecondaryFixed = miuixColorScheme.onSecondaryContainer,
+                        onSecondaryFixedVariant = miuixColorScheme.onSecondary,
+                        tertiaryFixed = miuixColorScheme.tertiaryContainer,
+                        tertiaryFixedDim = miuixColorScheme.tertiaryContainerVariant,
+                        onTertiaryFixed = miuixColorScheme.onTertiaryContainer,
+                        onTertiaryFixedVariant = miuixColorScheme.onTertiaryContainer
                     )
                 }
 
-                MiuixTheme(
-                    controller = controller
+                CompositionLocalProvider(
+                    LocalLegadoTypography provides legadoTypography,
+                    LocalLegadoColorScheme provides mappedColorScheme
                 ) {
-                    val miuixTextStyles = MiuixTheme.textStyles
-                    val mappedTypography = remember(miuixTextStyles) {
-                        miuixStylesToM3Typography(miuixTextStyles)
-                    }
-
-                    CompositionLocalProvider(
-                        LocalLegadoTypography provides mappedTypography
-                    ) {
-                        AppBackground(darkTheme = darkTheme) {
-                            content()
-                        }
-                    }
+                    AppBackground(darkTheme = darkTheme) { content() }
                 }
             }
+        } else {
+            val Typography = Typography()
+            MaterialExpressiveTheme(
+                colorScheme = colorScheme,
+                typography = Typography,
+                motionScheme = MotionScheme.expressive(),
+                shapes = Shapes()
+            ) {
+                val legadoTypography = remember(Typography) {
+                    LegadoTypography(
+                        headlineLarge = Typography.headlineLarge,
+                        headlineLargeEmphasized = Typography.headlineLargeEmphasized,
+                        headlineMedium = Typography.headlineMedium,
+                        headlineMediumEmphasized = Typography.headlineMediumEmphasized,
+                        headlineSmall = Typography.headlineSmall,
+                        headlineSmallEmphasized = Typography.headlineSmallEmphasized,
+
+
+                        titleLarge = Typography.titleLarge,
+                        titleLargeEmphasized = Typography.titleLargeEmphasized,
+                        titleMedium = Typography.titleMedium,
+                        titleMediumEmphasized = Typography.titleMediumEmphasized,
+                        titleSmall = Typography.titleSmall,
+                        titleSmallEmphasized = Typography.titleSmallEmphasized,
+
+
+                        bodyLarge = Typography.bodyLarge,
+                        bodyLargeEmphasized = Typography.bodyLargeEmphasized,
+                        bodyMedium = Typography.bodyMedium,
+                        bodyMediumEmphasized = Typography.bodyMediumEmphasized,
+                        bodySmall = Typography.bodySmall,
+                        bodySmallEmphasized = Typography.bodySmallEmphasized,
+
+                        labelLarge = Typography.labelLarge,
+                        labelLargeEmphasized = Typography.labelLargeEmphasized,
+                        labelMedium = Typography.labelMedium,
+                        labelMediumEmphasized = Typography.labelMediumEmphasized,
+                        labelSmall = Typography.labelSmall,
+                        labelSmallEmphasized = Typography.labelSmallEmphasized
+                    )
+                }
+
+                val semanticColors = remember(colorScheme) {
+                    LegadoColorScheme(
+                        primary = colorScheme.primary,
+                        onPrimary = colorScheme.onPrimary,
+                        primaryContainer = colorScheme.primaryContainer,
+                        onPrimaryContainer = colorScheme.onPrimaryContainer,
+                        inversePrimary = colorScheme.inversePrimary,
+                        secondary = colorScheme.secondary,
+                        onSecondary = colorScheme.onSecondary,
+                        secondaryContainer = colorScheme.secondaryContainer,
+                        onSecondaryContainer = colorScheme.onSecondaryContainer,
+                        tertiary = colorScheme.tertiary,
+                        onTertiary = colorScheme.onTertiary,
+                        tertiaryContainer = colorScheme.tertiaryContainer,
+                        onTertiaryContainer = colorScheme.onTertiaryContainer,
+                        background = colorScheme.background,
+                        onBackground = colorScheme.onBackground,
+                        surface = colorScheme.surface,
+                        onSurface = colorScheme.onSurface,
+                        surfaceVariant = colorScheme.surfaceVariant,
+                        onSurfaceVariant = colorScheme.onSurfaceVariant,
+                        surfaceTint = colorScheme.surfaceTint,
+                        inverseSurface = colorScheme.inverseSurface,
+                        inverseOnSurface = colorScheme.inverseOnSurface,
+                        error = colorScheme.error,
+                        onError = colorScheme.onError,
+                        errorContainer = colorScheme.errorContainer,
+                        onErrorContainer = colorScheme.onErrorContainer,
+                        outline = colorScheme.outline,
+                        outlineVariant = colorScheme.outlineVariant,
+                        scrim = colorScheme.scrim,
+                        surfaceBright = colorScheme.surfaceBright,
+                        surfaceDim = colorScheme.surfaceDim,
+                        surfaceContainer = colorScheme.surfaceContainer,
+                        surfaceContainerHigh = colorScheme.surfaceContainerHigh,
+                        surfaceContainerHighest = colorScheme.surfaceContainerHighest,
+                        surfaceContainerLow = colorScheme.surfaceContainerLow,
+                        surfaceContainerLowest = colorScheme.surfaceContainerLowest,
+                        primaryFixed = colorScheme.primaryFixed,
+                        primaryFixedDim = colorScheme.primaryFixedDim,
+                        onPrimaryFixed = colorScheme.onPrimaryFixed,
+                        onPrimaryFixedVariant = colorScheme.onPrimaryFixedVariant,
+                        secondaryFixed = colorScheme.secondaryFixed,
+                        secondaryFixedDim = colorScheme.secondaryFixedDim,
+                        onSecondaryFixed = colorScheme.onSecondaryFixed,
+                        onSecondaryFixedVariant = colorScheme.onSecondaryFixedVariant,
+                        tertiaryFixed = colorScheme.tertiaryFixed,
+                        tertiaryFixedDim = colorScheme.tertiaryFixedDim,
+                        onTertiaryFixed = colorScheme.onTertiaryFixed,
+                        onTertiaryFixedVariant = colorScheme.onTertiaryFixedVariant
+                    )
+                }
 
-            else -> {
-                MaterialExpressiveTheme(
-                    colorScheme = colorScheme,
-                    typography = Typography(),
-                    motionScheme = MotionScheme.expressive(),
-                    shapes = Shapes()
+                CompositionLocalProvider(
+                    LocalLegadoTypography provides legadoTypography,
+                    LocalLegadoColorScheme provides semanticColors
                 ) {
-                    AppBackground(darkTheme = darkTheme) {
-                        content()
-                    }
+                    AppBackground(darkTheme = darkTheme) { content() }
                 }
             }
         }
     }
-}
+}

+ 98 - 11
app/src/main/java/io/legado/app/ui/theme/LegadoTheme.kt

@@ -1,18 +1,18 @@
 package io.legado.app.ui.theme
 
 import androidx.compose.material3.ColorScheme
-import androidx.compose.material3.Typography
 import androidx.compose.material3.lightColorScheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.ReadOnlyComposable
 import androidx.compose.runtime.compositionLocalOf
 import androidx.compose.runtime.staticCompositionLocalOf
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
 import com.materialkolor.PaletteStyle
 import dev.chrisbanes.haze.HazeState
 import top.yukonga.miuix.kmp.theme.ColorSchemeMode
 
-data class LegadoThemeColors(
+data class LegadoThemeMode(
     val colorScheme: ColorScheme,
     val isDark: Boolean,
     val seedColor: Color,
@@ -22,30 +22,117 @@ data class LegadoThemeColors(
     val composeEngine: String,
 )
 
+data class LegadoColorScheme(
+    val primary: Color,
+    val onPrimary: Color,
+    val primaryContainer: Color,
+    val onPrimaryContainer: Color,
+    val inversePrimary: Color,
+    val secondary: Color,
+    val onSecondary: Color,
+    val secondaryContainer: Color,
+    val onSecondaryContainer: Color,
+    val tertiary: Color,
+    val onTertiary: Color,
+    val tertiaryContainer: Color,
+    val onTertiaryContainer: Color,
+    val background: Color,
+    val onBackground: Color,
+    val surface: Color,
+    val onSurface: Color,
+    val surfaceVariant: Color,
+    val onSurfaceVariant: Color,
+    val surfaceTint: Color,
+    val inverseSurface: Color,
+    val inverseOnSurface: Color,
+    val error: Color,
+    val onError: Color,
+    val errorContainer: Color,
+    val onErrorContainer: Color,
+    val outline: Color,
+    val outlineVariant: Color,
+    val scrim: Color,
+    val surfaceBright: Color,
+    val surfaceDim: Color,
+    val surfaceContainer: Color,
+    val surfaceContainerHigh: Color,
+    val surfaceContainerHighest: Color,
+    val surfaceContainerLow: Color,
+    val surfaceContainerLowest: Color,
+    val primaryFixed: Color,
+    val primaryFixedDim: Color,
+    val onPrimaryFixed: Color,
+    val onPrimaryFixedVariant: Color,
+    val secondaryFixed: Color,
+    val secondaryFixedDim: Color,
+    val onSecondaryFixed: Color,
+    val onSecondaryFixedVariant: Color,
+    val tertiaryFixed: Color,
+    val tertiaryFixedDim: Color,
+    val onTertiaryFixed: Color,
+    val onTertiaryFixedVariant: Color,
+)
+
+data class LegadoTypography(
+    val headlineLarge: TextStyle,
+    val headlineLargeEmphasized: TextStyle,
+    val headlineMedium: TextStyle,
+    val headlineMediumEmphasized: TextStyle,
+    val headlineSmall: TextStyle,
+    val headlineSmallEmphasized: TextStyle,
+
+    val titleLarge: TextStyle,
+    val titleLargeEmphasized: TextStyle,
+    val titleMedium: TextStyle,
+    val titleMediumEmphasized: TextStyle,
+    val titleSmall: TextStyle,
+    val titleSmallEmphasized: TextStyle,
+
+
+    val bodyLarge: TextStyle,
+    val bodyLargeEmphasized: TextStyle,
+    val bodyMedium: TextStyle,
+    val bodyMediumEmphasized: TextStyle,
+    val bodySmall: TextStyle,
+    val bodySmallEmphasized: TextStyle,
+
+    val labelLarge: TextStyle,
+    val labelLargeEmphasized: TextStyle,
+    val labelMedium: TextStyle,
+    val labelMediumEmphasized: TextStyle,
+    val labelSmall: TextStyle,
+    val labelSmallEmphasized: TextStyle,
+
+    )
+
+val LocalLegadoColorScheme = staticCompositionLocalOf<LegadoColorScheme> {
+    error("No ColorScheme provided")
+}
+
+val LocalLegadoTypography = staticCompositionLocalOf<LegadoTypography> {
+    error("No Typography provided")
+}
+
 val LocalLegadoThemeColors = staticCompositionLocalOf {
-    LegadoThemeColors(
+    LegadoThemeMode(
         colorScheme = lightColorScheme(),
         isDark = false,
         seedColor = Color.Unspecified,
         paletteStyle = PaletteStyle.TonalSpot,
         themeMode = ColorSchemeMode.System,
         useDynamicColor = true,
-        composeEngine = "m3"
+        composeEngine = "material"
     )
 }
 
-val LocalLegadoTypography = staticCompositionLocalOf {
-    Typography()
-}
-
 val LocalHazeState = compositionLocalOf<HazeState?> { null }
 
 object LegadoTheme {
 
-    val colorScheme: ColorScheme
+    val colorScheme: LegadoColorScheme
         @Composable
         @ReadOnlyComposable
-        get() = LocalLegadoThemeColors.current.colorScheme
+        get() = LocalLegadoColorScheme.current
 
     val isDark: Boolean
         @Composable
@@ -77,7 +164,7 @@ object LegadoTheme {
         @ReadOnlyComposable
         get() = LocalLegadoThemeColors.current.useDynamicColor
 
-    val typography: Typography
+    val typography: LegadoTypography
         @Composable
         @ReadOnlyComposable
         get() = LocalLegadoTypography.current

+ 3 - 2
app/src/main/java/io/legado/app/ui/theme/ThemeManager.kt

@@ -66,7 +66,8 @@ object ThemeManager {
             }
 
             AppThemeMode.CUSTOM -> {
-                CustomColorScheme(context.primaryColor, style)
+                val colorSpec = ThemeResolver.resolveColorSpecFromMaterialVersion(materialVersion)
+                CustomColorScheme(context.primaryColor, style, colorSpec)
                     .getColorScheme(darkTheme)
             }
 
@@ -93,4 +94,4 @@ object ThemeManager {
 
         return scheme
     }
-}
+}

+ 8 - 0
app/src/main/java/io/legado/app/ui/theme/ThemeResolver.kt

@@ -67,4 +67,12 @@ object ThemeResolver {
         }
     }
 
+    fun resolveColorSpecFromMaterialVersion(value: String?): ThemeColorSpec {
+        return when (value) {
+            "material3Expressive" -> ThemeColorSpec.SPEC_2025
+            "material3" -> ThemeColorSpec.SPEC_2021
+            else -> ThemeColorSpec.SPEC_2021
+        }
+    }
+
 }

+ 14 - 14
app/src/main/java/io/legado/app/ui/widget/CollapsibleHeader.kt

@@ -8,8 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.ExpandMore
-import androidx.compose.material3.Card
-import androidx.compose.material3.CardDefaults
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
@@ -22,26 +20,26 @@ import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.widget.components.card.GlassCard
 import io.legado.app.ui.widget.components.text.AppText
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
 fun CollapsibleHeader(
     modifier: Modifier = Modifier,
+    showIcon: Boolean = true,
     isCollapsed: Boolean,
     onToggle: () -> Unit,
     title: String,
     subtitle: String? = null,
     titleContent: (@Composable ColumnScope.() -> Unit)? = null
 ) {
-    Card(
+    GlassCard(
         modifier = modifier
             .fillMaxWidth()
             .padding(start = 16.dp, top = 4.dp, end = 16.dp),
         shape = MaterialTheme.shapes.medium,
-        colors = CardDefaults.cardColors(
-            containerColor = MaterialTheme.colorScheme.surfaceContainer
-        ),
+        containerColor = LegadoTheme.colorScheme.surfaceContainer,
         onClick = onToggle
     ) {
         Row(
@@ -59,7 +57,7 @@ fun CollapsibleHeader(
                         text = title,
                         style = LegadoTheme.typography.bodySmallEmphasized.copy(
                             fontWeight = FontWeight.Bold,
-                            color = MaterialTheme.colorScheme.primary
+                            color = LegadoTheme.colorScheme.primary
                         ),
                         maxLines = 1,
                         overflow = TextOverflow.Ellipsis
@@ -68,7 +66,7 @@ fun CollapsibleHeader(
                         AppText(
                             text = it,
                             style = LegadoTheme.typography.labelSmallEmphasized.copy(
-                                color = MaterialTheme.colorScheme.onSurfaceVariant
+                                color = LegadoTheme.colorScheme.onSurfaceVariant
                             ),
                             maxLines = 1,
                             overflow = TextOverflow.Ellipsis
@@ -82,12 +80,14 @@ fun CollapsibleHeader(
                 label = "arrowRotation"
             )
 
-            Icon(
-                imageVector = Icons.Default.ExpandMore,
-                contentDescription = if (isCollapsed) "展开" else "折叠",
-                modifier = Modifier.rotate(rotation),
-                tint = MaterialTheme.colorScheme.primary
-            )
+            if (showIcon) {
+                Icon(
+                    imageVector = Icons.Default.ExpandMore,
+                    contentDescription = if (isCollapsed) "展开" else "折叠",
+                    modifier = Modifier.rotate(rotation),
+                    tint = LegadoTheme.colorScheme.primary
+                )
+            }
         }
     }
 }

+ 0 - 49
app/src/main/java/io/legado/app/ui/widget/components/AppIcon.kt

@@ -1,49 +0,0 @@
-package io.legado.app.ui.widget.components
-
-
-import androidx.compose.material3.Icon
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.vector.ImageVector
-import io.legado.app.ui.theme.LegadoTheme.composeEngine
-import io.legado.app.ui.theme.ThemeResolver
-import androidx.compose.material3.LocalContentColor as MaterialLocalContentColor
-import top.yukonga.miuix.kmp.basic.Icon as MiuixIcon
-import top.yukonga.miuix.kmp.theme.LocalContentColor as MiuixLocalContentColor
-
-@Composable
-fun AppIcon(
-    imageVector: ImageVector,
-    contentDescription: String?,
-    modifier: Modifier = Modifier,
-    tint: Color = currentContentColor(),
-) {
-    val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
-
-    if (isMiuix) {
-        MiuixIcon(
-            imageVector = imageVector,
-            contentDescription = contentDescription,
-            modifier = modifier,
-            tint = tint
-        )
-    } else {
-        Icon(
-            imageVector = imageVector,
-            contentDescription = contentDescription,
-            modifier = modifier,
-            tint = tint
-        )
-    }
-}
-
-@Composable
-fun currentContentColor(): Color {
-    val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
-    return if (isMiuix) {
-        MiuixLocalContentColor.current
-    } else {
-        MaterialLocalContentColor.current
-    }
-}

+ 2 - 2
app/src/main/java/io/legado/app/ui/widget/components/AppNavigationBar.kt

@@ -5,6 +5,7 @@ import androidx.compose.material3.BottomAppBarDefaults
 import androidx.compose.material3.NavigationBar
 import androidx.compose.material3.NavigationBarItem
 import androidx.compose.material3.NavigationBarItemDefaults
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -12,7 +13,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
 import io.legado.app.ui.config.themeConfig.ThemeConfig
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeResolver
-import io.legado.app.ui.widget.components.text.AppText
 import top.yukonga.miuix.kmp.basic.NavigationBarDisplayMode
 import top.yukonga.miuix.kmp.theme.MiuixTheme
 import top.yukonga.miuix.kmp.basic.NavigationBar as MiuixNavigationBar
@@ -85,7 +85,7 @@ fun RowScope.AppNavigationBarItem(
             icon = m3Icon,
             colors = NavigationBarItemDefaults.colors(indicatorColor = m3IndicatorColor),
             label = if (m3ShowLabel) {
-                { AppText(labelString) }
+                { Text(labelString) }
             } else null,
             alwaysShowLabel = m3AlwaysShowLabel
         )

+ 127 - 0
app/src/main/java/io/legado/app/ui/widget/components/AppTextField.kt

@@ -0,0 +1,127 @@
+package io.legado.app.ui.widget.components
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.foundation.text.input.InputTransformation
+import androidx.compose.foundation.text.input.KeyboardActionHandler
+import androidx.compose.foundation.text.input.OutputTransformation
+import androidx.compose.foundation.text.input.TextFieldLineLimits
+import androidx.compose.foundation.text.input.TextFieldState
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.TextField
+import androidx.compose.material3.TextFieldColors
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.material3.TextFieldLabelPosition
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.takeOrElse
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.Density
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.ThemeResolver
+import io.legado.app.ui.widget.components.text.AppText
+import top.yukonga.miuix.kmp.theme.MiuixTheme
+import top.yukonga.miuix.kmp.basic.TextField as MiuixTextField
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun AppTextField(
+    state: TextFieldState,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    readOnly: Boolean = false,
+    // 1. 核心思想:保留 AppText 的统一样式与颜色降级参数
+    textStyle: TextStyle? = null,
+    textColor: Color = Color.Unspecified,
+    // 2. 将 Label 统一定义为 String 以兼容两套引擎
+    label: String? = null,
+    // 3. 保留 M3 TextField 的所有其他参数
+    labelPosition: TextFieldLabelPosition = TextFieldLabelPosition.Attached(),
+    placeholder: @Composable (() -> Unit)? = null,
+    leadingIcon: @Composable (() -> Unit)? = null,
+    trailingIcon: @Composable (() -> Unit)? = null,
+    prefix: @Composable (() -> Unit)? = null,
+    suffix: @Composable (() -> Unit)? = null,
+    supportingText: @Composable (() -> Unit)? = null,
+    isError: Boolean = false,
+    inputTransformation: InputTransformation? = null,
+    outputTransformation: OutputTransformation? = null,
+    keyboardOptions: KeyboardOptions = KeyboardOptions.Default,
+    onKeyboardAction: KeyboardActionHandler? = null,
+    lineLimits: TextFieldLineLimits = TextFieldLineLimits.Default,
+    onTextLayout: (Density.(getResult: () -> TextLayoutResult?) -> Unit)? = null,
+    scrollState: ScrollState = rememberScrollState(),
+    shape: Shape = TextFieldDefaults.shape,
+    colors: TextFieldColors = TextFieldDefaults.colors(),
+    contentPadding: PaddingValues? = null,
+    interactionSource: MutableInteractionSource? = null,
+) {
+    val baseStyle = textStyle ?: LegadoTheme.typography.bodyMedium
+    val defaultTextColor = LegadoTheme.colorScheme.onSurface
+    val finalTextColor = textColor.takeOrElse {
+        baseStyle.color.takeOrElse { defaultTextColor }
+    }
+    val mergedTextStyle = baseStyle.merge(color = finalTextColor)
+    val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
+
+    if (isMiuix) {
+        MiuixTextField(
+            state = state,
+            modifier = modifier,
+            enabled = enabled,
+            readOnly = readOnly,
+            textStyle = MiuixTheme.textStyles.main,
+            label = label ?: "",
+            leadingIcon = leadingIcon,
+            trailingIcon = trailingIcon,
+            inputTransformation = inputTransformation,
+            outputTransformation = outputTransformation,
+            keyboardOptions = keyboardOptions,
+            onKeyboardAction = onKeyboardAction,
+            lineLimits = lineLimits,
+            onTextLayout = onTextLayout,
+            scrollState = scrollState,
+            interactionSource = interactionSource
+        )
+    } else {
+        val resolvedContentPadding =
+            contentPadding ?: if (label == null || labelPosition is TextFieldLabelPosition.Above) {
+                TextFieldDefaults.contentPaddingWithoutLabel()
+            } else {
+                TextFieldDefaults.contentPaddingWithLabel()
+            }
+        TextField(
+            state = state,
+            modifier = modifier,
+            enabled = enabled,
+            readOnly = readOnly,
+            textStyle = mergedTextStyle,
+            labelPosition = labelPosition,
+            label = label?.let { { AppText(it) } },
+            placeholder = placeholder,
+            leadingIcon = leadingIcon,
+            trailingIcon = trailingIcon,
+            prefix = prefix,
+            suffix = suffix,
+            supportingText = supportingText,
+            isError = isError,
+            inputTransformation = inputTransformation,
+            outputTransformation = outputTransformation,
+            keyboardOptions = keyboardOptions,
+            onKeyboardAction = onKeyboardAction,
+            lineLimits = lineLimits,
+            onTextLayout = onTextLayout,
+            scrollState = scrollState,
+            shape = shape,
+            colors = colors,
+            contentPadding = resolvedContentPadding,
+            interactionSource = interactionSource
+        )
+    }
+}

+ 1 - 0
app/src/main/java/io/legado/app/ui/widget/components/EmptyMessageView.kt

@@ -24,6 +24,7 @@ import androidx.compose.ui.unit.TextUnit
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
 import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.widget.components.text.AnimatedTextLine
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable

+ 13 - 26
app/src/main/java/io/legado/app/ui/widget/components/GroupManageBottomSheet.kt

@@ -1,12 +1,10 @@
 package io.legado.app.ui.widget.components
 
 import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.items
 import androidx.compose.foundation.text.input.rememberTextFieldState
@@ -25,47 +23,36 @@ import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
-import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.button.SmallTextButton
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.settingItem.SettingItem
 import io.legado.app.ui.widget.components.text.AppText
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun GroupManageBottomSheet(
+    show: Boolean,
     groups: List<String>,
     onDismissRequest: () -> Unit,
     onUpdateGroup: (oldGroup: String, newGroup: String) -> Unit,
     onDeleteGroup: (group: String) -> Unit
 ) {
-    GlassModalBottomSheet(
-        onDismissRequest = onDismissRequest
+    AppModalBottomSheet(
+        show = show,
+        onDismissRequest = onDismissRequest,
+        title = stringResource(R.string.group_manage),
     ) {
-        Column(
-            modifier = Modifier
-                .padding(horizontal = 16.dp)
-                .padding(bottom = 16.dp),
-            horizontalAlignment = Alignment.CenterHorizontally,
-        ) {
-            AppText(
-                modifier = Modifier.padding(bottom = 16.dp),
-                text = stringResource(R.string.group_manage),
-                style = LegadoTheme.typography.titleMedium
-            )
-            LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
-                items(groups, key = { it }) { group ->
-                    GroupItem(
-                        group = group,
-                        onUpdateGroup = onUpdateGroup,
-                        onDeleteGroup = onDeleteGroup
-                    )
-                }
+        LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
+            items(groups, key = { it }) { group ->
+                GroupItem(
+                    group = group,
+                    onUpdateGroup = onUpdateGroup,
+                    onDeleteGroup = onDeleteGroup
+                )
             }
         }
     }

+ 30 - 100
app/src/main/java/io/legado/app/ui/widget/components/SearchBarSection.kt

@@ -1,32 +1,14 @@
 package io.legado.app.ui.widget.components
 
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.animateContentSize
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.scaleIn
-import androidx.compose.animation.scaleOut
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.lazy.LazyListState
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.text.input.TextFieldLineLimits
-import androidx.compose.foundation.text.input.clearText
 import androidx.compose.foundation.text.input.rememberTextFieldState
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Clear
-import androidx.compose.material.icons.filled.Search
-import androidx.compose.material.icons.filled.VerticalAlignTop
-import androidx.compose.material3.DropdownMenu
 import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.IconButton
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
-import androidx.compose.material3.TextField
 import androidx.compose.material3.TextFieldDefaults
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
@@ -37,14 +19,15 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.runtime.setValue
 import androidx.compose.runtime.snapshotFlow
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.dp
-import io.legado.app.ui.widget.components.button.SmallIconButton
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.ThemeResolver
+import io.legado.app.ui.widget.components.icon.AppIcon
+import io.legado.app.ui.widget.components.icon.AppIcons
 import io.legado.app.ui.widget.components.text.AppText
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
@@ -53,10 +36,10 @@ fun SearchBarSection(
     onQueryChange: (String) -> Unit,
     placeholder: String = "搜索...",
     leadingIcon: @Composable (() -> Unit)? = {
-        SmallIconButton(
-            icon = Icons.Default.Search,
-            contentDescription = null,
-            onClick = {})
+        AppIcon(
+            imageVector = AppIcons.Search,
+            contentDescription = null
+        )
     },
     backgroundColor: Color = MaterialTheme.colorScheme.surfaceContainerLow,
     scrollState: LazyListState? = null,
@@ -65,7 +48,6 @@ fun SearchBarSection(
     dropdownMenu: (@Composable (onDismiss: () -> Unit) -> Unit)? = null
 ) {
     var showMenu by remember { mutableStateOf(false) }
-
     val textFieldState = rememberTextFieldState(initialText = query)
 
     LaunchedEffect(query) {
@@ -91,79 +73,19 @@ fun SearchBarSection(
         }
     }
 
-    Surface(
-        modifier = Modifier
-            .fillMaxWidth()
-            .padding(start = 16.dp, end = 16.dp, bottom = 4.dp),
-        shape = RoundedCornerShape(32.dp),
-        color = backgroundColor
-    ) {
-        TextField(
+    val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
+
+    val searchTextField = @Composable {
+        AppTextField(
             state = textFieldState,
             modifier = Modifier
                 .fillMaxWidth()
-                .heightIn(min = 48.dp)
-                .padding(horizontal = 4.dp),
+                .then(
+                    if (isMiuix) Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
+                    else Modifier
+                ),
             placeholder = { AppText(placeholder) },
             leadingIcon = leadingIcon,
-            trailingIcon = {
-                Row(
-                    verticalAlignment = Alignment.CenterVertically,
-                    modifier = Modifier
-                        .padding(end = 4.dp)
-                        .animateContentSize()
-                ) {
-
-                    AnimatedVisibility(
-                        visible = textFieldState.text.isNotEmpty(),
-                        enter = fadeIn() + scaleIn(),
-                        exit = fadeOut() + scaleOut()
-                    ) {
-                        SmallIconButton(
-                            onClick = { textFieldState.clearText() },
-                            icon = Icons.Default.Clear,
-                            contentDescription = "清空输入"
-                        )
-                    }
-
-                    AnimatedVisibility(
-                        visible = showScrollToTop,
-                        enter = fadeIn() + scaleIn(),
-                        exit = fadeOut() + scaleOut()
-                    ) {
-                        SmallIconButton(
-                            onClick = {
-                                scope.launch {
-                                    scrollState?.animateScrollToItem(0)
-                                }
-                            },
-                            icon = Icons.Default.VerticalAlignTop,
-                            contentDescription = "回到顶部"
-                        )
-                    }
-
-                    if (trailingIcon != null) {
-                        Box {
-                            IconButton(
-                                onClick = {
-                                    if (dropdownMenu != null) showMenu = true
-                                }
-                            ) {
-                                trailingIcon()
-                            }
-
-                            if (dropdownMenu != null) {
-                                DropdownMenu(
-                                    expanded = showMenu,
-                                    onDismissRequest = { showMenu = false }
-                                ) {
-                                    dropdownMenu { showMenu = false }
-                                }
-                            }
-                        }
-                    }
-                }
-            },
             lineLimits = TextFieldLineLimits.SingleLine,
             colors = TextFieldDefaults.colors(
                 focusedContainerColor = Color.Transparent,
@@ -171,13 +93,21 @@ fun SearchBarSection(
                 disabledContainerColor = Color.Transparent,
                 focusedIndicatorColor = Color.Transparent,
                 unfocusedIndicatorColor = Color.Transparent,
-            ),
-            contentPadding = PaddingValues(
-                top = 4.dp,
-                bottom = 4.dp,
-                start = 12.dp,
-                end = 12.dp
             )
         )
     }
+
+    if (isMiuix) {
+        searchTextField()
+    } else {
+        Surface(
+            modifier = Modifier
+                .fillMaxWidth()
+                .padding(start = 16.dp, end = 16.dp, bottom = 4.dp),
+            shape = RoundedCornerShape(32.dp),
+            color = backgroundColor
+        ) {
+            searchTextField()
+        }
+    }
 }

+ 52 - 0
app/src/main/java/io/legado/app/ui/widget/components/ValueStepper.kt

@@ -0,0 +1,52 @@
+package io.legado.app.ui.widget.components
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Remove
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.widget.components.button.SmallOutlinedIconButton
+import io.legado.app.ui.widget.components.card.TextCard
+
+@Composable
+fun ValueStepper(
+    value: Float,
+    displayValue: Float,
+    valueRange: ClosedFloatingPointRange<Float>,
+    onValueChange: (Float) -> Unit,
+    modifier: Modifier = Modifier
+) {
+    Row(
+        modifier = modifier,
+        verticalAlignment = Alignment.CenterVertically,
+        horizontalArrangement = Arrangement.spacedBy(8.dp)
+    ) {
+        SmallOutlinedIconButton(
+            onClick = {
+                val newValue = (value.toInt() - 1).toFloat().coerceIn(valueRange)
+                onValueChange(newValue)
+            },
+            icon = Icons.Default.Remove
+        )
+        TextCard(
+            cornerRadius = 8.dp,
+            horizontalPadding = 8.dp,
+            verticalPadding = 4.dp,
+            text = displayValue.toInt().toString(),
+            backgroundColor = LegadoTheme.colorScheme.surfaceContainer,
+            contentColor = LegadoTheme.colorScheme.onSurface
+        )
+        SmallOutlinedIconButton(
+            onClick = {
+                val newValue = (value.toInt() + 1).toFloat().coerceIn(valueRange)
+                onValueChange(newValue)
+            },
+            icon = Icons.Default.Add
+        )
+    }
+}

+ 16 - 25
app/src/main/java/io/legado/app/ui/widget/components/bookmark/BookmarkEditSheet.kt

@@ -7,13 +7,10 @@ import androidx.compose.foundation.layout.Spacer
 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.material3.AlertDialog
-import androidx.compose.material3.Button
 import androidx.compose.material3.ButtonDefaults
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.OutlinedButton
 import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
@@ -25,13 +22,15 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import io.legado.app.data.entities.Bookmark
-import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.button.PrimaryButton
+import io.legado.app.ui.widget.components.button.SecondaryButton
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun BookmarkEditSheet(
+    show: Boolean,
     bookmark: Bookmark,
     onDismiss: () -> Unit,
     onSave: (Bookmark) -> Unit,
@@ -42,21 +41,16 @@ fun BookmarkEditSheet(
     var bookText by remember { mutableStateOf(bookmark.bookText) }
     var content by remember { mutableStateOf(bookmark.content) }
 
-    GlassModalBottomSheet(
-        onDismissRequest = onDismiss
+    AppModalBottomSheet(
+        show = show,
+        onDismissRequest = onDismiss,
+        title = bookmark.chapterName,
     ) {
         Column(
             modifier = Modifier
                 .fillMaxWidth()
-                .padding(16.dp)
                 .navigationBarsPadding()
         ) {
-            AppText(
-                text = bookmark.chapterName,
-                style = LegadoTheme.typography.titleMedium,
-                modifier = Modifier.padding(bottom = 16.dp)
-            )
-
             OutlinedTextField(
                 value = bookText,
                 onValueChange = { bookText = it },
@@ -82,15 +76,13 @@ fun BookmarkEditSheet(
                 horizontalArrangement = Arrangement.spacedBy(12.dp),
                 verticalAlignment = Alignment.CenterVertically
             ) {
-                OutlinedButton(
+                SecondaryButton(
                     onClick = { showDeleteConfirmDialog = true },
-                    colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.error),
-                    modifier = Modifier.weight(1f)
-                ) {
-                    AppText("删除")
-                }
+                    modifier = Modifier.weight(1f),
+                    text = "删除"
+                )
 
-                Button(
+                PrimaryButton(
                     onClick = {
                         val newBookmark = bookmark.apply {
                             this.bookText = bookText
@@ -98,10 +90,9 @@ fun BookmarkEditSheet(
                         }
                         onSave(newBookmark)
                     },
-                    modifier = Modifier.weight(1f)
-                ) {
-                    AppText("保存")
-                }
+                    modifier = Modifier.weight(1f),
+                    text = "保存"
+                )
             }
             Spacer(modifier = Modifier.height(16.dp))
         }

+ 4 - 5
app/src/main/java/io/legado/app/ui/widget/components/bookmark/BookmarkItem.kt

@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
@@ -31,7 +30,7 @@ fun BookmarkItem(
     onLongClick: () -> Unit
 ) {
     val backgroundColor by animateColorAsState(
-        targetValue = if (isDur) MaterialTheme.colorScheme.secondaryContainer.copy(alpha = 0.3f)
+        targetValue = if (isDur) LegadoTheme.colorScheme.secondaryContainer.copy(alpha = 0.3f)
         else Color.Transparent,
         label = "BgColor"
     )
@@ -53,7 +52,7 @@ fun BookmarkItem(
             AppText(
                 text = bookmark.chapterName,
                 style = LegadoTheme.typography.titleSmallEmphasized,
-                color = if (isDur) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary
+                color = if (isDur) LegadoTheme.colorScheme.primary else LegadoTheme.colorScheme.secondary
             )
 
             if (bookmark.bookText.isNotEmpty()) {
@@ -63,7 +62,7 @@ fun BookmarkItem(
                     style = LegadoTheme.typography.bodyMedium,
                     maxLines = 2,
                     overflow = TextOverflow.Ellipsis,
-                    color = MaterialTheme.colorScheme.onSurfaceVariant
+                    color = LegadoTheme.colorScheme.onSurfaceVariant
                 )
             }
 
@@ -72,7 +71,7 @@ fun BookmarkItem(
                 AppText(
                     text = bookmark.content,
                     style = LegadoTheme.typography.bodyMedium,
-                    color = MaterialTheme.colorScheme.primary
+                    color = LegadoTheme.colorScheme.primary
                 )
             }
         }

+ 74 - 0
app/src/main/java/io/legado/app/ui/widget/components/button/AppButton.kt

@@ -0,0 +1,74 @@
+package io.legado.app.ui.widget.components.button
+
+import androidx.compose.material3.Button
+import androidx.compose.material3.OutlinedButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import io.legado.app.ui.theme.LegadoTheme.composeEngine
+import io.legado.app.ui.theme.ThemeResolver
+import top.yukonga.miuix.kmp.basic.Button as MiuixButton
+import top.yukonga.miuix.kmp.basic.ButtonDefaults as MiuixButtonDefaults
+import top.yukonga.miuix.kmp.basic.Text as MiuixText
+
+@Composable
+fun PrimaryButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    text: String
+) {
+    if (ThemeResolver.isMiuixEngine(composeEngine)) {
+        MiuixButton(
+            onClick = onClick,
+            modifier = modifier,
+            enabled = enabled,
+            colors = MiuixButtonDefaults.buttonColorsPrimary()
+        ) {
+            MiuixText(
+                text = text
+            )
+        }
+    } else {
+        Button(
+            onClick = onClick,
+            modifier = modifier,
+            enabled = enabled,
+        ) {
+            Text(
+                text = text,
+            )
+        }
+    }
+}
+
+@Composable
+fun SecondaryButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    text: String
+) {
+    if (ThemeResolver.isMiuixEngine(composeEngine)) {
+        MiuixButton(
+            onClick = onClick,
+            modifier = modifier,
+            enabled = enabled,
+            colors = MiuixButtonDefaults.buttonColors()
+        ) {
+            MiuixText(
+                text = text
+            )
+        }
+    } else {
+        OutlinedButton(
+            onClick = onClick,
+            modifier = modifier,
+            enabled = enabled,
+        ) {
+            Text(
+                text = text
+            )
+        }
+    }
+}

+ 61 - 0
app/src/main/java/io/legado/app/ui/widget/components/button/AppIconButton.kt

@@ -0,0 +1,61 @@
+package io.legado.app.ui.widget.components.button
+
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconButtonColors
+import androidx.compose.material3.IconButtonDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.unit.Dp
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.ThemeResolver
+import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
+import top.yukonga.miuix.kmp.basic.IconButtonDefaults as MiuixIconButtonDefaults
+
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+fun AppIconButton(
+    onClick: () -> Unit,
+    modifier: Modifier = Modifier,
+    enabled: Boolean = true,
+    interactionSource: MutableInteractionSource? = null,
+    //M3
+    colors: IconButtonColors = IconButtonDefaults.iconButtonColors(),
+    shape: Shape = IconButtonDefaults.standardShape,
+    // MIUIX
+    holdDownState: Boolean = false,
+    miuixBackgroundColor: Color = Color.Unspecified,
+    miuixCornerRadius: Dp? = null,
+    miuixMinHeight: Dp? = null,
+    miuixMinWidth: Dp? = null,
+    content: @Composable () -> Unit
+) {
+    val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
+
+    if (isMiuix) {
+        MiuixIconButton(
+            onClick = onClick,
+            modifier = modifier,
+            enabled = enabled,
+            holdDownState = holdDownState,
+            backgroundColor = miuixBackgroundColor,
+            cornerRadius = MiuixIconButtonDefaults.CornerRadius,
+            minHeight = MiuixIconButtonDefaults.MinHeight,
+            minWidth = MiuixIconButtonDefaults.MinWidth,
+            content = content
+        )
+    } else {
+        IconButton(
+            onClick = onClick,
+            modifier = modifier,
+            enabled = enabled,
+            colors = colors,
+            interactionSource = interactionSource,
+            shape = shape,
+            content = content
+        )
+    }
+}

+ 4 - 4
app/src/main/java/io/legado/app/ui/widget/components/button/MiuixBackButton.kt → app/src/main/java/io/legado/app/ui/widget/components/button/MiuixButton.kt

@@ -7,21 +7,21 @@ import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.res.stringResource
 import io.legado.app.R
 import top.yukonga.miuix.kmp.basic.Icon
-import top.yukonga.miuix.kmp.basic.IconButton
 import top.yukonga.miuix.kmp.icon.MiuixIcons
 import top.yukonga.miuix.kmp.icon.extended.Back
 import top.yukonga.miuix.kmp.theme.LocalContentColor
 import top.yukonga.miuix.kmp.theme.MiuixTheme
+import top.yukonga.miuix.kmp.basic.IconButton as MiuixIconButton
 
 @Composable
-fun MiuixButton(
+fun MiuixIconButton(
     modifier: Modifier = Modifier,
     imageVector: ImageVector = MiuixIcons.Regular.Back,
     tint: Color = LocalContentColor.current,
     contentDescription: String? = stringResource(id = R.string.back),
     onClick: () -> Unit,
 ) {
-    IconButton(
+    MiuixIconButton(
         onClick = onClick,
         modifier = modifier
     ) {
@@ -44,7 +44,7 @@ fun MiuixToggleButton(
     uncheckedTint: Color = LocalContentColor.current,
     contentDescription: String? = null,
 ) {
-    IconButton(
+    MiuixIconButton(
         onClick = { onCheckedChange(!checked) },
         modifier = modifier
     ) {

+ 101 - 35
app/src/main/java/io/legado/app/ui/widget/components/button/SmallIconButton.kt

@@ -1,6 +1,8 @@
 package io.legado.app.ui.widget.components.button
 
 import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
@@ -80,8 +82,24 @@ fun SmallIconButton(
 fun SmallOutlinedIconButton(
     onClick: () -> Unit,
     icon: ImageVector,
+    modifier: Modifier = Modifier,
     contentDescription: String? = null
 ) {
+
+    val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
+    if (isMiuix) {
+        MiuixIconButton(
+            onClick = onClick,
+            modifier = modifier.size(32.dp),
+            backgroundColor = LegadoTheme.colorScheme.surfaceContainer
+        ) {
+            MiuixIcon(
+                imageVector = icon,
+                contentDescription = contentDescription,
+                modifier = Modifier.size(18.dp)
+            )
+        }
+    } else {
     CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
         OutlinedIconButton(
             onClick = onClick,
@@ -102,6 +120,7 @@ fun SmallOutlinedIconButton(
             )
         }
     }
+    }
 }
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -109,27 +128,42 @@ fun SmallOutlinedIconButton(
 fun SmallTonalIconButton(
     onClick: () -> Unit,
     icon: ImageVector,
+    modifier: Modifier = Modifier,
     contentDescription: String? = null
 ) {
-    CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
-        FilledTonalIconButton(
+    val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
+
+    if (isMiuix) {
+        MiuixIconButton(
             onClick = onClick,
-            modifier = Modifier.size(
-                IconButtonDefaults.extraSmallContainerSize(
-                    IconButtonDefaults.IconButtonWidthOption.Uniform
-                )
-            ),
-            shapes = IconButtonDefaults.shapes(),
-            colors = IconButtonDefaults.filledTonalIconButtonColors()
+            modifier = modifier.size(32.dp),
+            backgroundColor = LegadoTheme.colorScheme.surfaceContainer
         ) {
-            Icon(
+            MiuixIcon(
                 imageVector = icon,
                 contentDescription = contentDescription,
-                modifier = Modifier.size(
-                    IconButtonDefaults.extraSmallIconSize
-                )
+                modifier = Modifier.size(18.dp)
             )
         }
+    } else {
+        CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
+            FilledTonalIconButton(
+                onClick = onClick,
+                modifier = modifier.size(
+                    IconButtonDefaults.extraSmallContainerSize(
+                        IconButtonDefaults.IconButtonWidthOption.Uniform
+                    )
+                ),
+                shapes = IconButtonDefaults.shapes(),
+                colors = IconButtonDefaults.filledTonalIconButtonColors()
+            ) {
+                Icon(
+                    imageVector = icon,
+                    contentDescription = contentDescription,
+                    modifier = Modifier.size(IconButtonDefaults.extraSmallIconSize)
+                )
+            }
+        }
     }
 }
 
@@ -142,35 +176,67 @@ fun SmallOutlinedIconToggleButton(
     modifier: Modifier = Modifier,
     contentDescription: String? = null
 ) {
-    val defaultShape = IconButtonDefaults.extraSmallRoundShape
-    val pressedShape = IconButtonDefaults.extraSmallPressedShape
-    val checkedShape = IconButtonDefaults.extraSmallSelectedRoundShape
-
-    val toggleShapes = remember(defaultShape, checkedShape) {
-        IconToggleButtonShapes(
-            shape = defaultShape,
-            pressedShape = pressedShape,
-            checkedShape = checkedShape
+    val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
+
+    if (isMiuix) {
+
+        val containerColor by animateColorAsState(
+            targetValue = if (checked) LegadoTheme.colorScheme.primaryContainer else LegadoTheme.colorScheme.surfaceContainer,
+            animationSpec = tween(150),
+            label = "MiuixToggleContainerColor"
         )
-    }
 
-    CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
-        OutlinedIconToggleButton(
-            checked = checked,
-            onCheckedChange = onCheckedChange,
-            modifier = modifier.size(
-                IconButtonDefaults.extraSmallContainerSize(
-                    IconButtonDefaults.IconButtonWidthOption.Uniform
-                )
-            ),
-            shapes = toggleShapes
+        val iconTint by animateColorAsState(
+            targetValue = if (checked) LegadoTheme.colorScheme.onPrimaryContainer else LegadoTheme.colorScheme.onSurfaceVariant,
+            animationSpec = tween(150),
+            label = "MiuixToggleIconTint"
+        )
+
+        MiuixIconButton(
+            onClick = { onCheckedChange(!checked) },
+            modifier = modifier
+                .size(32.dp),
+            backgroundColor = containerColor
         ) {
-            Icon(
+            MiuixIcon(
                 imageVector = icon,
                 contentDescription = contentDescription,
-                modifier = Modifier.size(IconButtonDefaults.extraSmallIconSize),
+                tint = iconTint,
+                modifier = Modifier.size(18.dp)
             )
         }
+
+    } else {
+        val defaultShape = IconButtonDefaults.extraSmallRoundShape
+        val pressedShape = IconButtonDefaults.extraSmallPressedShape
+        val checkedShape = IconButtonDefaults.extraSmallSelectedRoundShape
+
+        val toggleShapes = remember(defaultShape, checkedShape) {
+            IconToggleButtonShapes(
+                shape = defaultShape,
+                pressedShape = pressedShape,
+                checkedShape = checkedShape
+            )
+        }
+
+        CompositionLocalProvider(LocalMinimumInteractiveComponentSize provides 0.dp) {
+            OutlinedIconToggleButton(
+                checked = checked,
+                onCheckedChange = onCheckedChange,
+                modifier = modifier.size(
+                    IconButtonDefaults.extraSmallContainerSize(
+                        IconButtonDefaults.IconButtonWidthOption.Uniform
+                    )
+                ),
+                shapes = toggleShapes
+            ) {
+                Icon(
+                    imageVector = icon,
+                    contentDescription = contentDescription,
+                    modifier = Modifier.size(IconButtonDefaults.extraSmallIconSize),
+                )
+            }
+        }
     }
 }
 

+ 33 - 16
app/src/main/java/io/legado/app/ui/widget/components/button/SmallTextButton.kt

@@ -12,7 +12,12 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.LegadoTheme.composeEngine
+import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.widget.components.text.AppText
+import top.yukonga.miuix.kmp.basic.Button as MiuixButton
+import top.yukonga.miuix.kmp.basic.ButtonDefaults as MiuixButtonDefaults
+import top.yukonga.miuix.kmp.basic.Text as MiuixText
 
 @Composable
 fun SmallTextButton(
@@ -21,21 +26,33 @@ fun SmallTextButton(
     modifier: Modifier = Modifier,
     onClick: () -> Unit
 ) {
-    TextButton(
-        onClick = onClick,
-        modifier = modifier,
-        contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
-        shape = MaterialTheme.shapes.small
-    ) {
-        Icon(
-            imageVector = icon,
-            contentDescription = null,
-            modifier = Modifier.size(16.dp)
-        )
-        Spacer(Modifier.width(4.dp))
-        AppText(
-            text = text,
-            style = LegadoTheme.typography.labelMedium
-        )
+    if (ThemeResolver.isMiuixEngine(composeEngine)) {
+        MiuixButton(
+            onClick = onClick,
+            modifier = modifier,
+            colors = MiuixButtonDefaults.buttonColors()
+        ) {
+            MiuixText(
+                text = text
+            )
+        }
+    } else {
+        TextButton(
+            onClick = onClick,
+            modifier = modifier,
+            contentPadding = PaddingValues(horizontal = 8.dp, vertical = 4.dp),
+            shape = MaterialTheme.shapes.small
+        ) {
+            Icon(
+                imageVector = icon,
+                contentDescription = null,
+                modifier = Modifier.size(16.dp)
+            )
+            Spacer(Modifier.width(4.dp))
+            AppText(
+                text = text,
+                style = LegadoTheme.typography.labelMedium
+            )
+        }
     }
 }

+ 4 - 3
app/src/main/java/io/legado/app/ui/widget/components/button/TopBarButton.kt

@@ -36,6 +36,7 @@ import androidx.compose.ui.unit.dp
 import io.legado.app.ui.config.themeConfig.ThemeConfig
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeResolver
+import io.legado.app.ui.widget.components.icon.AppIcons
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
 import kotlinx.coroutines.delay
@@ -107,12 +108,12 @@ fun TopBarButton(
 fun TopbarNavigationButton(
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
-    imageVector: ImageVector = Icons.AutoMirrored.Filled.ArrowBack,
+    imageVector: ImageVector = AppIcons.Back,
     contentDescription: String? = "返回",
     style: TopBarButtonVariant = TopBarButtonVariant.Filled
 ) {
     if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
-        MiuixButton(
+        MiuixIconButton(
             onClick = onClick,
             imageVector = imageVector,
             contentDescription = contentDescription,
@@ -138,7 +139,7 @@ fun TopBarActionButton(
 ) {
     val enableProgressive = ThemeConfig.enableProgressiveBlur
     if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
-        MiuixButton(
+        MiuixIconButton(
             onClick = onClick,
             imageVector = imageVector,
             contentDescription = contentDescription,

+ 0 - 1
app/src/main/java/io/legado/app/ui/widget/components/card/SelectionItemCard.kt

@@ -86,7 +86,6 @@ fun SelectionItemCard(
     GlassCard(
         onClick = onToggleSelection,
         modifier = modifier
-            .padding(horizontal = 16.dp)
             .fillMaxWidth(),
         shape = MaterialTheme.shapes.medium,
         containerColor = animatedContainerColor,

+ 4 - 17
app/src/main/java/io/legado/app/ui/widget/components/card/TextCard.kt

@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -17,11 +16,8 @@ import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.theme.LegadoTheme.composeEngine
-import io.legado.app.ui.theme.ThemeResolver
-import io.legado.app.ui.widget.components.AnimatedTextLine
-import io.legado.app.ui.widget.components.AppIcon
-import top.yukonga.miuix.kmp.theme.MiuixTheme
+import io.legado.app.ui.widget.components.icon.AppIcon
+import io.legado.app.ui.widget.components.text.AnimatedTextLine
 
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -40,17 +36,8 @@ fun TextCard(
     spacing: Dp = 4.dp,
     textStyle: TextStyle = LegadoTheme.typography.labelSmallEmphasized
 ) {
-    val isMiuix = ThemeResolver.isMiuixEngine(composeEngine)
-
-    val defaultBackground = if (isMiuix)
-        MiuixTheme.colorScheme.surfaceContainer
-    else
-        MaterialTheme.colorScheme.primaryContainer
-
-    val defaultContent = if (isMiuix)
-        MiuixTheme.colorScheme.onSurface
-    else
-        MaterialTheme.colorScheme.primary
+    val defaultBackground = LegadoTheme.colorScheme.surfaceContainer
+    val defaultContent = LegadoTheme.colorScheme.onSurface
 
     val finalBackgroundColor = backgroundColor ?: defaultBackground
     val finalContentColor = contentColor ?: defaultContent

+ 0 - 16
app/src/main/java/io/legado/app/ui/widget/components/cover/BookshelfCover.kt

@@ -7,17 +7,13 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Update
 import androidx.compose.material3.LinearProgressIndicator
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.StrokeCap
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.theme.LegadoTheme.composeEngine
-import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.widget.components.card.TextCard
-import top.yukonga.miuix.kmp.theme.MiuixTheme
 
 @Composable
 fun BookshelfCover(
@@ -31,16 +27,6 @@ fun BookshelfCover(
     sourceOrigin: String? = null,
     onLoadFinish: (() -> Unit)? = null
 ) {
-    val backgroundColor = if (ThemeResolver.isMiuixEngine(composeEngine)) {
-        MiuixTheme.colorScheme.primaryContainer.copy(alpha = 0.2f)
-    } else {
-        MaterialTheme.colorScheme.secondaryContainer
-    }
-    val contentColor = if (ThemeResolver.isMiuixEngine(composeEngine)) {
-        MiuixTheme.colorScheme.primary
-    } else {
-        MaterialTheme.colorScheme.onSecondaryContainer
-    }
     Box(modifier = modifier) {
         BookCover(
             name = name,
@@ -58,8 +44,6 @@ fun BookshelfCover(
                 modifier = Modifier
                     .align(Alignment.TopEnd)
                     .padding(2.dp),
-                backgroundColor = backgroundColor,
-                contentColor = contentColor,
                 cornerRadius = 4.dp,
                 horizontalPadding = 4.dp,
                 verticalPadding = 0.dp

+ 3 - 2
app/src/main/java/io/legado/app/ui/widget/components/dialog/ColorPickerSheet.kt

@@ -41,12 +41,13 @@ import com.github.skydoves.colorpicker.compose.HsvColorPicker
 import com.github.skydoves.colorpicker.compose.rememberColorPickerController
 import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun ColorPickerSheet(
+    show: Boolean,
     initialColor: Int,
     onDismissRequest: () -> Unit,
     onColorSelected: (Int) -> Unit
@@ -80,7 +81,7 @@ fun ColorPickerSheet(
         )
     }
 
-    GlassModalBottomSheet(onDismissRequest = onDismissRequest) {
+    AppModalBottomSheet(show = show, onDismissRequest = onDismissRequest) {
         Column(
             modifier = Modifier
                 .fillMaxWidth()

+ 24 - 12
app/src/main/java/io/legado/app/ui/widget/components/divider/PillDivider.kt

@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -14,26 +13,39 @@ import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.ThemeResolver
+import top.yukonga.miuix.kmp.theme.MiuixTheme
 
 @Composable
 fun PillDivider(
     modifier: Modifier = Modifier,
     thickness: Dp = 2.dp,
     widthFraction: Float = 0.2f,
-    color: Color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.6f)
+    color: Color = LegadoTheme.colorScheme.outlineVariant.copy(alpha = 0.6f)
 ) {
-    Box(
-        modifier = modifier
-            .fillMaxWidth()
-            .padding(vertical = 4.dp),
-        contentAlignment = Alignment.Center
-    ) {
+    val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
+    if (isMiuix) {
         Box(
             modifier = Modifier
-                .fillMaxWidth(widthFraction)
-                .height(thickness)
-                .clip(CircleShape)
-                .background(color)
+                .fillMaxWidth()
+                .height(4.dp)
+                .background(MiuixTheme.colorScheme.secondaryContainer)
         )
+    } else {
+        Box(
+            modifier = modifier
+                .fillMaxWidth()
+                .padding(vertical = 4.dp),
+            contentAlignment = Alignment.Center
+        ) {
+            Box(
+                modifier = Modifier
+                    .fillMaxWidth(widthFraction)
+                    .height(thickness)
+                    .clip(CircleShape)
+                    .background(color)
+            )
+        }
     }
 }

+ 2 - 3
app/src/main/java/io/legado/app/ui/widget/components/divider/PillHeaderDivider.kt

@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -24,8 +23,8 @@ fun PillHeaderDivider(
     title: String,
     modifier: Modifier = Modifier,
     dotSize: Dp = 2.dp,
-    dotColor: Color = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.8f),
-    titleColor: Color = MaterialTheme.colorScheme.primary
+    dotColor: Color = LegadoTheme.colorScheme.outlineVariant.copy(alpha = 0.8f),
+    titleColor: Color = LegadoTheme.colorScheme.primary
 ) {
     Row(
         modifier = modifier

+ 11 - 16
app/src/main/java/io/legado/app/ui/widget/components/filePicker/FilePickerSheet.kt

@@ -9,7 +9,6 @@ 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.material.icons.Icons
 import androidx.compose.material.icons.automirrored.filled.InsertDriveFile
@@ -17,7 +16,6 @@ import androidx.compose.material.icons.filled.CloudUpload
 import androidx.compose.material.icons.filled.EditNote
 import androidx.compose.material.icons.filled.FolderOpen
 import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.runtime.Composable
@@ -28,7 +26,8 @@ import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.icon.AppIcon
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 
 enum class FilePickerSheetMode {
@@ -38,6 +37,7 @@ enum class FilePickerSheetMode {
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun FilePickerSheet(
+    show: Boolean,
     onDismissRequest: () -> Unit,
     title: String = stringResource(R.string.select_operation),
     onSelectSysDir: (() -> Unit)? = null,
@@ -47,22 +47,17 @@ fun FilePickerSheet(
     onUpload: (() -> Unit)? = null,
     allowExtensions: Array<String>? = null,
 ) {
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
+        title = title,
         onDismissRequest = onDismissRequest,
-        containerColor = { it.surface }
+        containerColor = LegadoTheme.colorScheme.surface
     ) {
         Column(
             modifier = Modifier
-                .fillMaxWidth()
-                .padding(horizontal = 16.dp, vertical = 8.dp),
+                .fillMaxWidth(),
             horizontalAlignment = Alignment.CenterHorizontally
         ) {
-            AppText(
-                text = title,
-                style = LegadoTheme.typography.titleMedium,
-                modifier = Modifier.padding(bottom = 20.dp)
-            )
-
             FlowRow(
                 modifier = Modifier.fillMaxWidth(),
                 horizontalArrangement = Arrangement.spacedBy(12.dp),
@@ -127,7 +122,7 @@ private fun RowScope.FilePickerOptionCard(
             .weight(1f)
             .height(100.dp),
         shape = MaterialTheme.shapes.medium,
-        color = MaterialTheme.colorScheme.surfaceContainerLow,
+        color = LegadoTheme.colorScheme.surfaceContainerLow,
         tonalElevation = 2.dp
     ) {
         Column(
@@ -135,11 +130,11 @@ private fun RowScope.FilePickerOptionCard(
             horizontalAlignment = Alignment.CenterHorizontally,
             verticalArrangement = Arrangement.Center
         ) {
-            Icon(
+            AppIcon(
                 imageVector = icon,
                 contentDescription = null,
                 modifier = Modifier.size(32.dp),
-                tint = MaterialTheme.colorScheme.primary
+                tint = LegadoTheme.colorScheme.primary
             )
             Spacer(modifier = Modifier.height(8.dp))
             AppText(

+ 88 - 0
app/src/main/java/io/legado/app/ui/widget/components/icon/AppIcon.kt

@@ -0,0 +1,88 @@
+package io.legado.app.ui.widget.components.icon
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.size
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.paint
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.isUnspecified
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.graphics.takeOrElse
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import io.legado.app.ui.theme.LegadoTheme
+
+/**
+ * 1. 基础 AppIcon 实现
+ */
+@Composable
+fun AppIcon(
+    painter: Painter,
+    contentDescription: String?,
+    modifier: Modifier = Modifier,
+    tint: Color = Color.Unspecified,
+) {
+    val defaultTintColor = LegadoTheme.colorScheme.onSurface
+    val finalTint = tint.takeOrElse { defaultTintColor }
+
+    val colorFilter = remember(finalTint) {
+        if (finalTint == Color.Unspecified) null else ColorFilter.tint(finalTint)
+    }
+
+    val semantics = if (contentDescription != null) {
+        Modifier.semantics {
+            this.contentDescription = contentDescription
+            this.role = Role.Image
+        }
+    } else {
+        Modifier
+    }
+
+    Box(
+        modifier
+            .defaultIconSize(painter)
+            .paint(painter, colorFilter = colorFilter, contentScale = ContentScale.Fit)
+            .then(semantics)
+    )
+}
+
+@Composable
+fun AppIcon(
+    imageVector: ImageVector?,
+    contentDescription: String?,
+    modifier: Modifier = Modifier,
+    tint: Color = Color.Unspecified,
+) {
+    if (imageVector == null) return
+
+    AppIcon(
+        painter = rememberVectorPainter(imageVector),
+        contentDescription = contentDescription,
+        modifier = modifier,
+        tint = tint,
+    )
+}
+
+/**
+ * 3. 辅助扩展函数:处理图标的默认尺寸
+ * M3 和 MIUIX 内部都有 defaultSizeFor,如果 painter 没有固有尺寸,默认设为 24.dp
+ */
+private fun Modifier.defaultIconSize(painter: Painter): Modifier {
+    return this.then(
+        if (painter.intrinsicSize == Size.Unspecified || painter.intrinsicSize.isUnspecified) {
+            Modifier.size(24.dp)
+        } else {
+            Modifier
+        }
+    )
+}

+ 54 - 0
app/src/main/java/io/legado/app/ui/widget/components/icon/AppIcons.kt

@@ -0,0 +1,54 @@
+package io.legado.app.ui.widget.components.icon
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material.icons.filled.Clear
+import androidx.compose.material.icons.filled.Replay
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.vector.ImageVector
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.ThemeResolver
+import top.yukonga.miuix.kmp.icon.MiuixIcons
+import top.yukonga.miuix.kmp.icon.basic.Search
+import top.yukonga.miuix.kmp.icon.extended.Back
+import top.yukonga.miuix.kmp.icon.extended.Close
+import top.yukonga.miuix.kmp.icon.extended.Refresh
+
+/**
+ * 应用全局图标映射层
+ */
+object AppIcons {
+
+    private val isMiuix: Boolean
+        @Composable
+        get() = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
+
+    val Search: ImageVector
+        @Composable
+        get() = if (isMiuix) {
+            MiuixIcons.Basic.Search
+        } else {
+            Icons.Default.Search
+        }
+
+    val Close: ImageVector
+        @Composable
+        get() = if (isMiuix) {
+            MiuixIcons.Close
+        } else {
+            Icons.Default.Clear
+        }
+
+    val Back: ImageVector
+        @Composable
+        get() = if (isMiuix) {
+            MiuixIcons.Back
+        } else {
+            Icons.AutoMirrored.Filled.ArrowBack
+        }
+
+    val Replay: ImageVector
+        @Composable
+        get() = if (isMiuix) MiuixIcons.Refresh ?: Icons.Default.Replay else Icons.Default.Replay
+}

+ 72 - 107
app/src/main/java/io/legado/app/ui/widget/components/importComponents/ImportComponents.kt

@@ -1,10 +1,6 @@
 package io.legado.app.ui.widget.components.importComponents
 
 import android.annotation.SuppressLint
-import androidx.compose.animation.animateColorAsState
-import androidx.compose.animation.animateContentSize
-import androidx.compose.animation.core.FastOutSlowInEasing
-import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
@@ -26,16 +22,10 @@ import androidx.compose.material.icons.filled.SelectAll
 import androidx.compose.material3.AlertDialog
 import androidx.compose.material3.AssistChip
 import androidx.compose.material3.Button
-import androidx.compose.material3.Card
-import androidx.compose.material3.CardDefaults
 import androidx.compose.material3.CenterAlignedTopAppBar
-import androidx.compose.material3.Checkbox
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
-import androidx.compose.material3.ListItem
-import androidx.compose.material3.ListItemDefaults
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.OutlinedButton
 import androidx.compose.material3.OutlinedTextField
@@ -44,7 +34,6 @@ import androidx.compose.material3.SnackbarHost
 import androidx.compose.material3.SnackbarHostState
 import androidx.compose.material3.TextButton
 import androidx.compose.material3.TopAppBarDefaults
-import androidx.compose.material3.rememberModalBottomSheetState
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -59,9 +48,11 @@ import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.ui.theme.LegadoTheme
-import io.legado.app.ui.widget.components.AnimatedText
 import io.legado.app.ui.widget.components.AppScaffold
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.button.SmallIconButton
+import io.legado.app.ui.widget.components.card.SelectionItemCard
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
+import io.legado.app.ui.widget.components.text.AnimatedText
 import io.legado.app.ui.widget.components.text.AppText
 import kotlinx.coroutines.launch
 
@@ -116,31 +107,42 @@ fun SourceInputDialog(
     )
 }
 
+//TODO: 动画
 @SuppressLint("ConfigurationScreenWidthHeight")
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun <T> BatchImportDialog(
     title: String,
-    importState: BaseImportUiState.Success<T>,
+    importState: BaseImportUiState<T>,
     onDismissRequest: () -> Unit,
-    onConfirm: (List<T>) -> Unit, // 返回选中的原始数据列表
+    onConfirm: (List<T>) -> Unit,
     onToggleItem: (index: Int) -> Unit,
     onToggleAll: (isSelected: Boolean) -> Unit,
     onItemInfoClick: (index: Int) -> Unit = {},
-    // 插槽:允许调用方自定义顶部菜单
     topBarActions: @Composable RowScope.() -> Unit = {},
-    // 插槽:自定义每一行的显示内容
-    itemContent: @Composable (data: T, status: ImportStatus) -> Unit
+    itemTitle: (data: T) -> String,
+    itemSubtitle: (data: T) -> String? = { null }
 ) {
-    val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
+    val show = importState is BaseImportUiState.Success<T>
+
+    var cachedState by remember { mutableStateOf<BaseImportUiState.Success<T>?>(null) }
+    if (importState is BaseImportUiState.Success<T>) {
+        cachedState = importState
+    }
+
+    if (!show && cachedState == null) return
+
     val scope = rememberCoroutineScope()
     val snackbarHostState = remember { SnackbarHostState() }
-    val selectedCount = importState.items.count { it.isSelected }
-    val totalCount = importState.items.size
 
-    GlassModalBottomSheet(
+    val currentState = cachedState!!
+    val selectedCount = currentState.items.count { it.isSelected }
+    val totalCount = currentState.items.size
+
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismissRequest,
-        containerColor = { it.surfaceContainer }
+        containerColor = LegadoTheme.colorScheme.surfaceContainer
     ) {
         AppScaffold(
             modifier = Modifier
@@ -171,44 +173,34 @@ fun <T> BatchImportDialog(
                 ImportBottomBar(
                     selectedCount = selectedCount,
                     totalCount = totalCount,
-                    onToggleSelectAll = { isAll -> onToggleAll(isAll) },
+                    onToggleSelectAll = onToggleAll,
                     onConfirm = {
-                        scope.launch { sheetState.hide() }.invokeOnCompletion {
-                            if (!sheetState.isVisible) {
-                                val selectedData =
-                                    importState.items.filter { it.isSelected }.map { it.data }
-                                onConfirm(selectedData)
-                            }
-                        }
+                        val selectedData =
+                            currentState.items.filter { it.isSelected }.map { it.data }
+                        onConfirm(selectedData)
                     },
-                    onCancel = {
-                        scope.launch { sheetState.hide() }.invokeOnCompletion {
-                            if (!sheetState.isVisible) {
-                                onDismissRequest()
-                            }
-                        }
-                    }
+                    onCancel = onDismissRequest
                 )
             }
         ) { padding ->
             LazyColumn(
-                modifier = Modifier
-                    .padding(padding),
+                modifier = Modifier.padding(padding),
                 contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
                 verticalArrangement = Arrangement.spacedBy(8.dp)
             ) {
-                itemsIndexed(importState.items, key = { _, item -> item.data.hashCode() }) { index, itemWrapper ->
+                itemsIndexed(
+                    currentState.items,
+                    key = { _, item -> item.data.hashCode() }) { index, itemWrapper ->
                     ImportItemRow(
+                        title = itemTitle(itemWrapper.data),
+                        subtitle = itemSubtitle(itemWrapper.data),
                         isSelected = itemWrapper.isSelected,
                         status = itemWrapper.status,
                         onClick = { onToggleItem(index) },
                         onInfoClick = {
-                            scope.launch {
-                                snackbarHostState.showSnackbar("其实还没写桀桀桀")
-                            }
+                            scope.launch { snackbarHostState.showSnackbar("其实还没写桀桀桀") }
                             onItemInfoClick(index)
-                        },
-                        content = { itemContent(itemWrapper.data, itemWrapper.status) }
+                        }
                     )
                 }
             }
@@ -216,74 +208,47 @@ fun <T> BatchImportDialog(
     }
 }
 
-@OptIn(ExperimentalMaterial3Api::class)
 @Composable
-private fun ImportItemRow(
+fun ImportItemRow(
+    title: String,
+    subtitle: String? = null,
     isSelected: Boolean,
     status: ImportStatus,
     onClick: () -> Unit,
-    onInfoClick: () -> Unit,
-    content: @Composable () -> Unit
+    onInfoClick: () -> Unit
 ) {
-    val containerColor by animateColorAsState(
-        targetValue = if (isSelected)
-            MaterialTheme.colorScheme.secondaryContainer
-        else
-            MaterialTheme.colorScheme.surface,
-        animationSpec = tween(
-            durationMillis = 200,
-            easing = FastOutSlowInEasing
-        ),
-        label = "CardColor"
-    )
+    SelectionItemCard(
+        title = title,
+        subtitle = subtitle,
+        isSelected = isSelected,
+        inSelectionMode = true,
+        onToggleSelection = onClick,
+        modifier = Modifier.padding(vertical = 4.dp),
+        trailingAction = {
+            AppText(
+                text = when (status) {
+                    ImportStatus.New -> "新增"
+                    ImportStatus.Update -> "更新"
+                    ImportStatus.Existing -> "已有"
+                    ImportStatus.Error -> "错误"
+                },
+                style = LegadoTheme.typography.labelMedium,
+                color = when (status) {
+                    ImportStatus.New -> MaterialTheme.colorScheme.primary
+                    ImportStatus.Update -> MaterialTheme.colorScheme.secondary
+                    ImportStatus.Error -> MaterialTheme.colorScheme.error
+                    else -> MaterialTheme.colorScheme.outline
+                },
+                modifier = Modifier.padding(end = 4.dp)
+            )
 
-    Card(
-        onClick = onClick,
-        modifier = Modifier.fillMaxWidth(),
-        shape = MaterialTheme.shapes.medium,
-        colors = CardDefaults.cardColors(
-            containerColor = containerColor
-        )
-    ) {
-        ListItem(
-            modifier = Modifier.animateContentSize(),
-            headlineContent = { content() },
-            leadingContent = {
-                Checkbox(
-                    checked = isSelected,
-                    onCheckedChange = null
-                )
-            },
-            supportingContent = {
-                AppText(
-                    text = when (status) {
-                        ImportStatus.New -> "新增"
-                        ImportStatus.Update -> "更新"
-                        ImportStatus.Existing -> "已有"
-                        ImportStatus.Error -> "错误"
-                    },
-                    style = LegadoTheme.typography.labelMedium,
-                    color = when (status) {
-                        ImportStatus.New -> MaterialTheme.colorScheme.primary
-                        ImportStatus.Update -> MaterialTheme.colorScheme.secondary
-                        ImportStatus.Error -> MaterialTheme.colorScheme.error
-                        else -> MaterialTheme.colorScheme.outline
-                    }
-                )
-            },
-            trailingContent = {
-                IconButton(onClick = onInfoClick) {
-                    Icon(
-                        imageVector = Icons.Default.Info,
-                        contentDescription = "详情"
-                    )
-                }
-            },
-            colors = ListItemDefaults.colors(
-                containerColor = Color.Transparent
+            SmallIconButton(
+                onClick = onInfoClick,
+                icon = Icons.Default.Info,
+                contentDescription = "详情"
             )
-        )
-    }
+        }
+    )
 }
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)

+ 2 - 1
app/src/main/java/io/legado/app/ui/widget/components/lazylist/VerticalFastScroller.kt

@@ -46,6 +46,7 @@ import androidx.compose.ui.util.fastFirstOrNull
 import androidx.compose.ui.util.fastForEach
 import androidx.compose.ui.util.fastLastOrNull
 import androidx.compose.ui.util.fastMaxBy
+import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.widget.components.lazylist.Scroller.STICKY_HEADER_KEY_PREFIX
 import kotlinx.coroutines.channels.BufferOverflow
 import kotlinx.coroutines.flow.MutableSharedFlow
@@ -67,7 +68,7 @@ fun VerticalFastScroller(
     listState: LazyListState,
     modifier: Modifier = Modifier,
     thumbAllowed: () -> Boolean = { true },
-    thumbColor: Color = MaterialTheme.colorScheme.primary,
+    thumbColor: Color = LegadoTheme.colorScheme.primary,
     topContentPadding: Dp = Dp.Hairline,
     bottomContentPadding: Dp = Dp.Hairline,
     endContentPadding: Dp = Dp.Hairline,

+ 42 - 18
app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenu.kt

@@ -3,6 +3,8 @@ package io.legado.app.ui.widget.components.menuItem
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
 import androidx.compose.material3.DropdownMenu
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.MaterialExpressiveTheme
@@ -15,8 +17,11 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.theme.rememberOpaqueColorScheme
-
+import top.yukonga.miuix.kmp.basic.ListPopupColumn
+import top.yukonga.miuix.kmp.extra.SuperListPopup
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
@@ -24,31 +29,50 @@ fun RoundDropdownMenu(
     expanded: Boolean,
     onDismissRequest: () -> Unit,
     modifier: Modifier = Modifier,
+    // M3 专属视觉参数,Miuix 模式下会优雅降级(忽略)
     shape: Shape = MaterialTheme.shapes.medium,
     shadowElevation: Dp = 4.dp,
     verticalSpacing: Dp = 8.dp,
     content: @Composable ColumnScope.(dismiss: () -> Unit) -> Unit
 ) {
-    val colorScheme = rememberOpaqueColorScheme()
+    val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
+
+    if (isMiuix) {
+        SuperListPopup(
+            show = expanded,
+            onDismissRequest = onDismissRequest,
+            popupModifier = modifier
+        ) {
+            ListPopupColumn {
+                Column() {
+                    Spacer(Modifier.height(12.dp))
+                    content(onDismissRequest)
+                    Spacer(Modifier.height(12.dp))
+                }
+            }
+        }
+    } else {
+        val colorScheme = rememberOpaqueColorScheme()
 
-    DropdownMenu(
-        expanded = expanded,
-        onDismissRequest = onDismissRequest,
-        modifier = modifier,
-        shape = shape,
-        shadowElevation = shadowElevation,
-        containerColor = colorScheme.surfaceContainerLow
-    ) {
-        MaterialExpressiveTheme(
-            colorScheme = colorScheme,
-            typography = Typography(),
-            motionScheme = MotionScheme.expressive(),
-            shapes = Shapes()
+        DropdownMenu(
+            expanded = expanded,
+            onDismissRequest = onDismissRequest,
+            modifier = modifier,
+            shape = shape,
+            shadowElevation = shadowElevation,
+            containerColor = colorScheme.surfaceContainerLow
         ) {
-            Column(
-                verticalArrangement = Arrangement.spacedBy(verticalSpacing)
+            MaterialExpressiveTheme(
+                colorScheme = colorScheme,
+                typography = Typography(),
+                motionScheme = MotionScheme.expressive(),
+                shapes = Shapes()
             ) {
-                content(onDismissRequest)
+                Column(
+                    verticalArrangement = Arrangement.spacedBy(verticalSpacing)
+                ) {
+                    content(onDismissRequest)
+                }
             }
         }
     }

+ 89 - 24
app/src/main/java/io/legado/app/ui/widget/components/menuItem/RoundDropdownMenuItem.kt

@@ -1,6 +1,8 @@
 package io.legado.app.ui.widget.components.menuItem
 
+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.PaddingValues
 import androidx.compose.foundation.layout.Row
@@ -11,54 +13,114 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.layout.widthIn
-import androidx.compose.material3.Icon
 import androidx.compose.material3.LocalContentColor
 import androidx.compose.material3.LocalTextStyle
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.MenuDefaults
 import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawBehind
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.text.font.FontWeight
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.theme.rememberOpaqueColorScheme
+import io.legado.app.ui.widget.components.icon.AppIcon
+import top.yukonga.miuix.kmp.theme.MiuixTheme
+import top.yukonga.miuix.kmp.basic.Text as MiuixText
 
 @Composable
 fun RoundDropdownMenuItem(
-    text: @Composable () -> Unit,
+    text: String,
+    color: Color = LegadoTheme.colorScheme.surface,
     onClick: () -> Unit,
     modifier: Modifier = Modifier,
+    isSelected: Boolean = false,
     leadingIcon: @Composable (() -> Unit)? = null,
     trailingIcon: @Composable (() -> Unit)? = null,
     enabled: Boolean = true,
     contentPadding: PaddingValues = MenuDefaults.DropdownMenuItemContentPadding,
     interactionSource: MutableInteractionSource? = null,
 ) {
-    val colorScheme = rememberOpaqueColorScheme()
+    val isMiuix = ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)
     val interaction = interactionSource ?: remember { MutableInteractionSource() }
 
-    val contentColor =
-        if (enabled) MaterialTheme.colorScheme.onSurface
-        else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
+    if (isMiuix) {
+        val backgroundColor = if (isSelected) {
+            LegadoTheme.colorScheme.primaryContainer
+        } else {
+            Color.Transparent
+        }
+
+        val contentColor = if (isSelected) {
+            LegadoTheme.colorScheme.onPrimaryContainer
+        } else {
+            LegadoTheme.colorScheme.onSurface
+        }
+
+        Row(
+            verticalAlignment = Alignment.CenterVertically,
+            horizontalArrangement = Arrangement.SpaceBetween,
+            modifier = modifier
+                .fillMaxWidth()
+                .drawBehind { drawRect(backgroundColor) }
+                .clickable(
+                    interactionSource = interaction,
+                    indication = androidx.compose.foundation.LocalIndication.current,
+                    enabled = enabled,
+                    onClick = onClick
+                )
+                .padding(horizontal = 20.dp, vertical = 12.dp)
+        ) {
+            CompositionLocalProvider(
+                LocalContentColor provides contentColor,
+                LocalTextStyle provides MiuixTheme.textStyles.body1
+            ) {
+                if (leadingIcon != null) {
+                    leadingIcon()
+                    Spacer(Modifier.width(12.dp))
+                }
+
+                Box(
+                    modifier = Modifier.weight(1f),
+                    contentAlignment = Alignment.CenterStart
+                ) {
+                    MiuixText(
+                        text = text,
+                        fontWeight = FontWeight.Medium
+                    )
+                }
+
+                if (trailingIcon != null) {
+                    Spacer(Modifier.width(12.dp))
+                    trailingIcon()
+                }
+            }
+        }
+    } else {
+        val colorScheme = rememberOpaqueColorScheme()
+        val contentColor = if (enabled) {
+            MaterialTheme.colorScheme.onSurface
+        } else {
+            MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
+        }
 
-    Surface(
-        onClick = onClick,
-        modifier = modifier
-            .padding(horizontal = 8.dp)
-            .fillMaxWidth(),
-        enabled = enabled,
-        shape = MaterialTheme.shapes.small,
-        color = colorScheme.surface,
-        interactionSource = interaction
-    ) {
-        CompositionLocalProvider(
-            LocalContentColor provides contentColor,
-            LocalTextStyle provides LegadoTheme.typography.labelLarge
+        Surface(
+            onClick = onClick,
+            modifier = modifier
+                .padding(horizontal = 8.dp)
+                .fillMaxWidth(),
+            enabled = enabled,
+            shape = MaterialTheme.shapes.small,
+            color = colorScheme.surface,
+            interactionSource = interaction
         ) {
             Row(
                 modifier = Modifier
@@ -67,17 +129,20 @@ fun RoundDropdownMenuItem(
                     .widthIn(min = 120.dp),
                 verticalAlignment = Alignment.CenterVertically
             ) {
-
                 if (leadingIcon != null) {
                     leadingIcon()
-                    Spacer(Modifier.width(8.dp))
+                    Spacer(Modifier.width(12.dp))
                 }
 
                 Box(
                     modifier = Modifier.weight(1f),
                     contentAlignment = Alignment.CenterStart
                 ) {
-                    text()
+                    Text(
+                        modifier = Modifier.widthIn(max = 200.dp),
+                        text = text,
+                        style = LegadoTheme.typography.bodyMediumEmphasized
+                    )
                 }
 
                 if (trailingIcon != null) {
@@ -94,9 +159,9 @@ fun MenuItemIcon(
     imageVector: ImageVector,
     modifier: Modifier = Modifier,
     contentDescription: String? = null,
-    tint: Color = LocalContentColor.current
+    tint: Color = Color.Unspecified
 ) {
-    Icon(
+    AppIcon(
         imageVector = imageVector,
         contentDescription = contentDescription,
         modifier = modifier.size(18.dp),

+ 138 - 0
app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/AppModalBottomSheet.kt

@@ -0,0 +1,138 @@
+package io.legado.app.ui.widget.components.modalBottomSheet
+
+import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.BottomSheetDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.MaterialExpressiveTheme
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.MotionScheme
+import androidx.compose.material3.Shapes
+import androidx.compose.material3.Text
+import androidx.compose.material3.Typography
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.platform.LocalWindowInfo
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.DpSize
+import androidx.compose.ui.unit.dp
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.ThemeResolver
+import io.legado.app.ui.theme.rememberOpaqueColorScheme
+import top.yukonga.miuix.kmp.extra.WindowBottomSheet
+
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
+@Composable
+fun AppModalBottomSheet(
+    show: Boolean,
+    onDismissRequest: () -> Unit,
+    modifier: Modifier = Modifier,
+    title: String? = null,
+    startAction: @Composable (() -> Unit)? = null,
+    endAction: @Composable (() -> Unit)? = null,
+    containerColor: Color = LegadoTheme.colorScheme.surfaceContainerLow,
+    content: @Composable ColumnScope.() -> Unit
+) {
+    if (ThemeResolver.isMiuixEngine(LegadoTheme.composeEngine)) {
+        WindowBottomSheet(
+            show = show,
+            modifier = modifier,
+            title = title,
+            startAction = startAction,
+            endAction = endAction,
+            insideMargin = DpSize(16.dp, 12.dp),
+            backgroundColor = containerColor,
+            onDismissRequest = onDismissRequest,
+            onDismissFinished = onDismissRequest,
+            enableWindowDim = true,
+            allowDismiss = true
+        ) {
+            Column(
+                modifier = Modifier.fillMaxWidth(),
+                content = content
+            )
+        }
+    } else {
+        if (show) {
+            val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
+            val colorScheme = rememberOpaqueColorScheme()
+            val density = LocalDensity.current
+            val maxHeight = with(density) {
+                LocalWindowInfo.current.containerSize.height.toDp() * 0.8f
+            }
+
+            ModalBottomSheet(
+                onDismissRequest = onDismissRequest,
+                sheetState = sheetState,
+                containerColor = containerColor,
+                contentColor = colorScheme.onSurface,
+                dragHandle = { BottomSheetDefaults.DragHandle() }
+            ) {
+                MaterialExpressiveTheme(
+                    colorScheme = colorScheme,
+                    typography = Typography(),
+                    motionScheme = MotionScheme.expressive(),
+                    shapes = Shapes()
+                ) {
+                    Column(
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .padding(horizontal = 16.dp)
+                            .heightIn(max = maxHeight)
+                            .animateContentSize()
+                            .then(modifier)
+                    ) {
+                        val hasHeader =
+                            !title.isNullOrEmpty() || startAction != null || endAction != null
+
+                        if (hasHeader) {
+                            Box(
+                                modifier = Modifier
+                                    .fillMaxWidth()
+                                    .padding(bottom = 16.dp),
+                                contentAlignment = Alignment.Center
+                            ) {
+                                if (startAction != null) {
+                                    Box(modifier = Modifier.align(Alignment.CenterStart)) {
+                                        startAction()
+                                    }
+                                }
+
+                                if (!title.isNullOrEmpty()) {
+                                    Text(
+                                        text = title,
+                                        style = LegadoTheme.typography.titleMediumEmphasized,
+                                        color = colorScheme.onSurface,
+                                        textAlign = TextAlign.Center,
+                                        maxLines = 1,
+                                        overflow = TextOverflow.Ellipsis,
+                                        modifier = Modifier.padding(horizontal = 56.dp)
+                                    )
+                                }
+
+                                if (endAction != null) {
+                                    Box(modifier = Modifier.align(Alignment.CenterEnd)) {
+                                        endAction()
+                                    }
+                                }
+                            }
+                        }
+
+                        content()
+                    }
+                }
+            }
+        }
+    }
+}

+ 0 - 64
app/src/main/java/io/legado/app/ui/widget/components/modalBottomSheet/GlassModalBottomSheet.kt

@@ -1,64 +0,0 @@
-package io.legado.app.ui.widget.components.modalBottomSheet
-
-import androidx.compose.animation.animateContentSize
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.ColumnScope
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.material3.ColorScheme
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
-import androidx.compose.material3.MaterialExpressiveTheme
-import androidx.compose.material3.ModalBottomSheet
-import androidx.compose.material3.MotionScheme
-import androidx.compose.material3.Shapes
-import androidx.compose.material3.SheetState
-import androidx.compose.material3.Typography
-import androidx.compose.material3.rememberModalBottomSheetState
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalWindowInfo
-import io.legado.app.ui.theme.rememberOpaqueColorScheme
-
-@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
-@Composable
-fun GlassModalBottomSheet(
-    onDismissRequest: () -> Unit,
-    modifier: Modifier = Modifier,
-    sheetState: SheetState = rememberModalBottomSheetState(
-        skipPartiallyExpanded = true
-    ),
-    containerColor: (ColorScheme) -> Color = { it.surfaceContainerLow },
-    content: @Composable ColumnScope.() -> Unit
-) {
-    val colorScheme = rememberOpaqueColorScheme()
-    val density = LocalDensity.current
-    val maxHeight = with(density) {
-        LocalWindowInfo.current.containerSize.height.toDp() * 0.8f
-    }
-
-    ModalBottomSheet(
-        onDismissRequest = onDismissRequest,
-        sheetState = sheetState,
-        containerColor = containerColor(colorScheme),
-        contentColor = colorScheme.onSurface
-    ) {
-        MaterialExpressiveTheme(
-            colorScheme = colorScheme,
-            typography = Typography(),
-            motionScheme = MotionScheme.expressive(),
-            shapes = Shapes()
-        ) {
-            Column(
-                modifier = Modifier
-                    .fillMaxWidth()
-                    .heightIn(max = maxHeight)
-                    .animateContentSize()
-                    .then(modifier),
-                content = content
-            )
-        }
-    }
-}

+ 4 - 2
app/src/main/java/io/legado/app/ui/widget/components/rules/RuleEditSheet.kt

@@ -35,7 +35,7 @@ import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
 import io.legado.app.R
-import io.legado.app.ui.widget.components.modalBottomSheet.GlassModalBottomSheet
+import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
 import io.legado.app.ui.widget.components.text.AppText
 import kotlinx.coroutines.launch
 
@@ -52,6 +52,7 @@ data class RuleEditFields(
 @OptIn(ExperimentalMaterial3Api::class)
 @Composable
 fun <T> RuleEditSheet(
+    show: Boolean,
     rule: T?,
     title: String,
     label1: String,
@@ -74,7 +75,8 @@ fun <T> RuleEditSheet(
 
     fun getCurrentEntity() = fromFields(RuleEditFields(name, rule1, rule2), rule)
 
-    GlassModalBottomSheet(
+    AppModalBottomSheet(
+        show = show,
         onDismissRequest = onDismissRequest
     ) {
         Box(modifier = Modifier.fillMaxWidth()) {

+ 166 - 109
app/src/main/java/io/legado/app/ui/widget/components/settingItem/CompactSettingItems.kt

@@ -1,13 +1,12 @@
 package io.legado.app.ui.widget.components.settingItem
 
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Row
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Add
 import androidx.compose.material.icons.filled.Check
-import androidx.compose.material.icons.filled.Remove
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
 import androidx.compose.material3.MaterialTheme
@@ -20,17 +19,22 @@ import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.scale
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.unit.dp
-import io.legado.app.ui.widget.components.button.SmallOutlinedIconButton
+import io.legado.app.ui.theme.LegadoTheme
+import io.legado.app.ui.theme.LegadoTheme.composeEngine
+import io.legado.app.ui.theme.ThemeResolver
+import io.legado.app.ui.widget.components.ValueStepper
 import io.legado.app.ui.widget.components.card.TextCard
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
-import io.legado.app.ui.widget.components.text.AppText
+import top.yukonga.miuix.kmp.basic.BasicComponent
+import top.yukonga.miuix.kmp.extra.SuperSwitch
+import top.yukonga.miuix.kmp.extra.WindowDropdown
+import top.yukonga.miuix.kmp.basic.Slider as MiuixSlider
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
 @Composable
@@ -41,49 +45,73 @@ fun CompactDropdownSettingItem(
     entryValues: Array<String>,
     description: String? = null,
     imageVector: ImageVector? = null,
-    color: Color? = MaterialTheme.colorScheme.surface,
+    color: Color? = LegadoTheme.colorScheme.surface,
     shape: Shape = MaterialTheme.shapes.small,
     onValueChange: (String) -> Unit
 ) {
-    val currentEntry = displayEntries.getOrNull(entryValues.indexOf(selectedValue)) ?: selectedValue
+    if (ThemeResolver.isMiuixEngine(composeEngine)) {
+        val selectedIndex = entryValues.indexOf(selectedValue).coerceAtLeast(0)
+        val spinnerItems = displayEntries.toList()
 
-    SettingItem(
-        title = title,
-        description = description,
-        imageVector = imageVector,
-        color = color,
-        shape = shape,
-        trailingContent = {
-            TextCard(
-                cornerRadius = 8.dp,
-                horizontalPadding = 8.dp,
-                verticalPadding = 4.dp,
-                text = currentEntry,
-                backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
-                contentColor = MaterialTheme.colorScheme.onSurface
-            )
-        },
-        dropdownMenu = { onDismiss ->
-            displayEntries.forEachIndexed { index, display ->
-                RoundDropdownMenuItem(
-                    text = { AppText(display) },
-                    onClick = {
-                        onValueChange(entryValues[index])
-                        onDismiss()
-                    },
-                    trailingIcon = if (selectedValue == entryValues[index]) {
-                        {
-                            Icon(
-                                Icons.Default.Check,
-                                contentDescription = null,
-                                modifier = Modifier.size(18.dp)
-                            )
-                        }
-                    } else null
+        WindowDropdown(
+            title = title,
+            summary = description,
+            items = spinnerItems,
+            selectedIndex = selectedIndex,
+            startAction = imageVector?.let { icon ->
+                {
+                    Icon(
+                        imageVector = icon,
+                        contentDescription = null
+                    )
+                }
+            },
+            onSelectedIndexChange = { index ->
+                onValueChange(entryValues[index])
+            }
+        )
+    } else {
+        val currentEntry =
+            displayEntries.getOrNull(entryValues.indexOf(selectedValue)) ?: selectedValue
+
+        SettingItem(
+            title = title,
+            description = description,
+            imageVector = imageVector,
+            color = color,
+            shape = shape,
+            trailingContent = {
+                TextCard(
+                    cornerRadius = 8.dp,
+                    horizontalPadding = 8.dp,
+                    verticalPadding = 4.dp,
+                    text = currentEntry,
+                    backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
+                    contentColor = MaterialTheme.colorScheme.onSurface
                 )
+            },
+            dropdownMenu = { onDismiss ->
+                displayEntries.forEachIndexed { index, display ->
+                    RoundDropdownMenuItem(
+                        text = display,
+                        onClick = {
+                            onValueChange(entryValues[index])
+                            onDismiss()
+                        },
+                        trailingIcon = if (selectedValue == entryValues[index]) {
+                            {
+                                Icon(
+                                    Icons.Default.Check,
+                                    contentDescription = null,
+                                    modifier = Modifier.size(18.dp)
+                                )
+                            }
+                        } else null
+                    )
+                }
             }
-        }
-    )
+        )
+    }
 }
 
 @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@@ -104,59 +132,78 @@ fun CompactSliderSettingItem(
     var sliderValue by remember(value) { mutableFloatStateOf(value) }
     var displayValue by remember(value) { mutableFloatStateOf(value) }
 
-    SettingItem(
-        title = title,
-        description = description,
-        imageVector = imageVector,
-        color = color,
-        shape = shape,
-        expanded = expanded,
-        onExpandChange = { expanded = it },
-        trailingContent = {
-            Row(
-                verticalAlignment = Alignment.CenterVertically,
-                horizontalArrangement = Arrangement.spacedBy(8.dp)
-            ) {
-                SmallOutlinedIconButton(
-                    onClick = {
-                        val newValue = (value.toInt() - 1).toFloat().coerceIn(valueRange)
-                        onValueChange(newValue)
-                    },
-                    icon = Icons.Default.Remove
-                )
-                TextCard(
-                    cornerRadius = 8.dp,
-                    horizontalPadding = 8.dp,
-                    verticalPadding = 4.dp,
-                    text = displayValue.toInt().toString(),
-                    backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
-                    contentColor = MaterialTheme.colorScheme.onSurface
+    if (ThemeResolver.isMiuixEngine(composeEngine)) {
+        Column(modifier = Modifier.fillMaxWidth()) {
+            BasicComponent(
+                title = title,
+                summary = description,
+                onClick = { expanded = !expanded },
+                endActions = {
+                    ValueStepper(
+                        value = value,
+                        displayValue = displayValue,
+                        valueRange = valueRange,
+                        onValueChange = onValueChange
+                    )
+                }
+            )
+
+            AnimatedVisibility(visible = expanded) {
+                Column(
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .padding(horizontal = 16.dp, vertical = 8.dp)
+                ) {
+                    MiuixSlider(
+                        value = sliderValue,
+                        onValueChange = {
+                            sliderValue = it
+                            displayValue = it
+                        },
+                        onValueChangeFinished = {
+                            onValueChange(sliderValue)
+                        },
+                        valueRange = valueRange,
+                        steps = steps,
+                        modifier = Modifier.fillMaxWidth()
+                    )
+                }
+            }
+        }
+    } else {
+        SettingItem(
+            title = title,
+            description = description,
+            imageVector = imageVector,
+            color = color,
+            shape = shape,
+            expanded = expanded,
+            onExpandChange = { expanded = it },
+            trailingContent = {
+                ValueStepper(
+                    value = value,
+                    displayValue = displayValue,
+                    valueRange = valueRange,
+                    onValueChange = onValueChange
                 )
-                SmallOutlinedIconButton(
-                    onClick = {
-                        val newValue = (value.toInt() + 1).toFloat().coerceIn(valueRange)
-                        onValueChange(newValue)
+            },
+            expandContent = {
+                Slider(
+                    value = sliderValue,
+                    onValueChange = {
+                        sliderValue = it
+                        displayValue = it
+                    },
+                    onValueChangeFinished = {
+                        onValueChange(sliderValue)
                     },
-                    icon = Icons.Default.Add
+                    valueRange = valueRange,
+                    steps = steps,
+                    modifier = Modifier.fillMaxWidth()
                 )
             }
-        },
-        expandContent = {
-            Slider(
-                value = sliderValue,
-                onValueChange = {
-                    sliderValue = it
-                    displayValue = it
-                },
-                onValueChangeFinished = {
-                    onValueChange(sliderValue)
-                },
-                valueRange = valueRange,
-                steps = steps,
-                modifier = Modifier.fillMaxWidth()
-            )
-        }
-    )
+        )
+    }
 
     LaunchedEffect(value) {
         if (!expanded) {
@@ -177,20 +224,30 @@ fun CompactSwitchSettingItem(
     enabled: Boolean = true,
     onCheckedChange: (Boolean) -> Unit
 ) {
-    SettingItem(
-        title = title,
-        description = description,
-        imageVector = imageVector,
-        color = color,
-        shape = shape,
-        onClick = { if (enabled) onCheckedChange(!checked) },
-        trailingContent = {
-            Switch(
-                modifier = Modifier.scale(0.8f),
-                checked = checked,
-                onCheckedChange = onCheckedChange,
-                enabled = enabled
-            )
-        }
-    )
+    if (ThemeResolver.isMiuixEngine(composeEngine)) {
+        SuperSwitch(
+            title = title,
+            summary = description,
+            checked = checked,
+            onCheckedChange = onCheckedChange,
+            enabled = enabled,
+        )
+    } else {
+        SettingItem(
+            title = title,
+            description = description,
+            imageVector = imageVector,
+            color = color,
+            shape = shape,
+            onClick = { if (enabled) onCheckedChange(!checked) },
+            trailingContent = {
+                Switch(
+                    modifier = Modifier.scale(0.8f),
+                    checked = checked,
+                    onCheckedChange = onCheckedChange,
+                    enabled = enabled
+                )
+            }
+        )
+    }
 }

+ 1 - 2
app/src/main/java/io/legado/app/ui/widget/components/settingItem/ListSettingItem.kt

@@ -11,7 +11,6 @@ import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
-import io.legado.app.ui.widget.components.text.AppText
 import top.yukonga.miuix.kmp.basic.SpinnerEntry
 import top.yukonga.miuix.kmp.extra.SuperSpinner
 
@@ -64,7 +63,7 @@ fun DropdownListSettingItem(
             dropdownMenu = { onDismiss ->
                 displayEntries.forEachIndexed { index, display ->
                     RoundDropdownMenuItem(
-                        text = { AppText(display) },
+                        text = display,
                         onClick = {
                             onValueChange(entryValues[index])
                             onDismiss()

+ 4 - 15
app/src/main/java/io/legado/app/ui/widget/components/settingItem/SwitchSettingItem.kt

@@ -6,8 +6,7 @@ import androidx.compose.ui.graphics.vector.ImageVector
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.widget.components.AdaptiveSwitch
-import top.yukonga.miuix.kmp.basic.BasicComponent
-import top.yukonga.miuix.kmp.basic.Switch
+import top.yukonga.miuix.kmp.extra.SuperSwitch
 
 
 @Composable
@@ -23,22 +22,12 @@ fun SwitchSettingItem(
     val composeEngine = LegadoTheme.composeEngine
 
     if (ThemeResolver.isMiuixEngine(composeEngine)) {
-        val toggleAction = {
-            if (enabled) onCheckedChange(!checked)
-        }
-
-        BasicComponent(
+        SuperSwitch(
             title = title,
             summary = description,
+            checked = checked,
+            onCheckedChange = onCheckedChange,
             enabled = enabled,
-            onClick = toggleAction,
-            endActions = {
-                Switch(
-                    checked = checked,
-                    onCheckedChange = onCheckedChange,
-                    enabled = enabled
-                )
-            }
         )
     } else {
         SettingItem(

+ 3 - 3
app/src/main/java/io/legado/app/ui/widget/components/tabRow/AdaptiveTabRow.kt → app/src/main/java/io/legado/app/ui/widget/components/tabRow/AppTabRow.kt

@@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.PrimaryScrollableTabRow
 import androidx.compose.material3.Tab
+import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
@@ -11,12 +12,11 @@ import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.ThemeResolver
-import io.legado.app.ui.widget.components.text.AppText
 import top.yukonga.miuix.kmp.basic.TabRowDefaults
 import top.yukonga.miuix.kmp.basic.TabRowWithContour
 
 @Composable
-fun AdaptiveTabRow(
+fun AppTabRow(
     tabTitles: List<String>,
     selectedTabIndex: Int,
     onTabSelected: (Int) -> Unit,
@@ -48,7 +48,7 @@ fun AdaptiveTabRow(
                     selected = selectedTabIndex == index,
                     onClick = { onTabSelected(index) },
                     text = {
-                        AppText(
+                        Text(
                             text = title,
                             maxLines = 1,
                             overflow = TextOverflow.Ellipsis,

+ 1 - 2
app/src/main/java/io/legado/app/ui/widget/components/AnimatedText.kt → app/src/main/java/io/legado/app/ui/widget/components/text/AnimatedText.kt

@@ -1,4 +1,4 @@
-package io.legado.app.ui.widget.components
+package io.legado.app.ui.widget.components.text
 
 import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.fadeIn
@@ -22,7 +22,6 @@ import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.TextUnit
-import io.legado.app.ui.widget.components.text.AppText
 
 @Composable
 fun AnimatedText(

+ 52 - 77
app/src/main/java/io/legado/app/ui/widget/components/text/AppText.kt

@@ -1,11 +1,11 @@
 package io.legado.app.ui.widget.components.text
 
 
-import androidx.compose.material3.LocalTextStyle
-import androidx.compose.material3.Text
+import androidx.compose.foundation.text.BasicText
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.takeOrElse
 import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.text.TextLayoutResult
 import androidx.compose.ui.text.TextStyle
@@ -16,10 +16,7 @@ import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.text.style.TextDecoration
 import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.TextUnit
-import io.legado.app.ui.theme.LegadoTheme.composeEngine
-import io.legado.app.ui.theme.ThemeResolver
-import top.yukonga.miuix.kmp.theme.MiuixTheme
-import top.yukonga.miuix.kmp.basic.Text as MiuixText
+import io.legado.app.ui.theme.LegadoTheme
 
 @Composable
 fun AppText(
@@ -41,53 +38,41 @@ fun AppText(
     onTextLayout: ((TextLayoutResult) -> Unit)? = null,
     style: TextStyle? = null,
 ) {
+    val baseStyle = style ?: LegadoTheme.typography.bodyMedium
 
-    if (ThemeResolver.isMiuixEngine(composeEngine)) {
-        MiuixText(
-            text = text,
-            modifier = modifier,
-            color = color,
+    // 2. 获取默认文本色:直接拿你自己封装的 LegadoTheme.colorScheme.onSurface
+    val defaultTextColor = LegadoTheme.colorScheme.onSurface
+
+    // 3. 颜色降级逻辑:传入的 color -> style 中的 color -> 主题默认色
+    val finalTextColor = color.takeOrElse {
+        baseStyle.color.takeOrElse { defaultTextColor }
+    }
+
+    BasicText(
+        text = text,
+        modifier = modifier,
+        style = baseStyle.merge(
+            color = finalTextColor,
             fontSize = fontSize,
-            fontStyle = fontStyle,
             fontWeight = fontWeight,
+            textAlign = textAlign ?: TextAlign.Unspecified,
+            lineHeight = lineHeight,
             fontFamily = fontFamily,
-            letterSpacing = letterSpacing,
             textDecoration = textDecoration,
-            textAlign = textAlign,
-            lineHeight = lineHeight,
-            overflow = overflow,
-            softWrap = softWrap,
-            maxLines = maxLines,
-            minLines = minLines,
-            onTextLayout = onTextLayout,
-            style = style ?: MiuixTheme.textStyles.main
-        )
-    } else {
-        Text(
-            text = text,
-            modifier = modifier,
-            color = color,
-            fontSize = fontSize,
             fontStyle = fontStyle,
-            fontWeight = fontWeight,
-            fontFamily = fontFamily,
             letterSpacing = letterSpacing,
-            textDecoration = textDecoration,
-            textAlign = textAlign,
-            lineHeight = lineHeight,
-            overflow = overflow,
-            softWrap = softWrap,
-            maxLines = maxLines,
-            minLines = minLines,
-            onTextLayout = onTextLayout ?: {},
-            style = style ?: LocalTextStyle.current
-        )
-    }
+        ),
+        onTextLayout = onTextLayout,
+        overflow = overflow,
+        softWrap = softWrap,
+        maxLines = maxLines,
+        minLines = minLines
+    )
 }
 
 @Composable
 fun AppText(
-    text: AnnotatedString,
+    text: AnnotatedString, // 接收 AnnotatedString
     modifier: Modifier = Modifier,
     color: Color = Color.Unspecified,
     fontSize: TextUnit = TextUnit.Unspecified,
@@ -105,46 +90,36 @@ fun AppText(
     onTextLayout: ((TextLayoutResult) -> Unit)? = null,
     style: TextStyle? = null,
 ) {
+    // 1. 获取基础样式:如果外部没传 style,直接拿你自己封装的 LegadoTheme.typography.bodyMedium
+    // 这完美避开了 M3 的 LocalTextStyle,且自动适配 Miuix/M3 引擎!
+    val baseStyle = style ?: LegadoTheme.typography.bodyMedium
 
-    if (ThemeResolver.isMiuixEngine(composeEngine)) {
-        MiuixText(
-            text = text,
-            modifier = modifier,
-            color = color,
+    // 2. 获取默认文本色:直接拿你自己封装的 LegadoTheme.colorScheme.onSurface
+    val defaultTextColor = LegadoTheme.colorScheme.onSurface
+
+    // 3. 颜色降级逻辑:传入的 color -> style 中的 color -> 主题默认色
+    val finalTextColor = color.takeOrElse {
+        baseStyle.color.takeOrElse { defaultTextColor }
+    }
+
+    BasicText(
+        text = text,
+        modifier = modifier,
+        style = baseStyle.merge(
+            color = finalTextColor,
             fontSize = fontSize,
-            fontStyle = fontStyle,
             fontWeight = fontWeight,
+            textAlign = textAlign ?: TextAlign.Unspecified,
+            lineHeight = lineHeight,
             fontFamily = fontFamily,
-            letterSpacing = letterSpacing,
             textDecoration = textDecoration,
-            textAlign = textAlign,
-            lineHeight = lineHeight,
-            overflow = overflow,
-            softWrap = softWrap,
-            maxLines = maxLines,
-            minLines = minLines,
-            onTextLayout = onTextLayout ?: {},
-            style = style ?: MiuixTheme.textStyles.main
-        )
-    } else {
-        Text(
-            text = text,
-            modifier = modifier,
-            color = color,
-            fontSize = fontSize,
             fontStyle = fontStyle,
-            fontWeight = fontWeight,
-            fontFamily = fontFamily,
             letterSpacing = letterSpacing,
-            textDecoration = textDecoration,
-            textAlign = textAlign,
-            lineHeight = lineHeight,
-            overflow = overflow,
-            softWrap = softWrap,
-            maxLines = maxLines,
-            minLines = minLines,
-            onTextLayout = onTextLayout ?: {},
-            style = style ?: LocalTextStyle.current
-        )
-    }
+        ),
+        onTextLayout = onTextLayout,
+        overflow = overflow,
+        softWrap = softWrap,
+        maxLines = maxLines,
+        minLines = minLines
+    )
 }

+ 3 - 6
app/src/main/java/io/legado/app/ui/widget/components/topbar/DynamicTopAppBar.kt

@@ -10,12 +10,9 @@ import androidx.compose.foundation.layout.ColumnScope
 import androidx.compose.foundation.layout.RowScope
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.filled.ArrowBack
-import androidx.compose.material.icons.filled.Close
 import androidx.compose.material.icons.filled.MoreVert
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
@@ -23,12 +20,13 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
-import io.legado.app.ui.widget.components.AnimatedTextLine
 import io.legado.app.ui.widget.components.SearchBarSection
 import io.legado.app.ui.widget.components.button.TopBarActionButton
 import io.legado.app.ui.widget.components.button.TopbarNavigationButton
+import io.legado.app.ui.widget.components.icon.AppIcons
 import io.legado.app.ui.widget.components.list.ListUiState
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
+import io.legado.app.ui.widget.components.text.AnimatedTextLine
 
 
 @OptIn(ExperimentalMaterial3Api::class)
@@ -70,7 +68,7 @@ fun <T> DynamicTopAppBar(
             if (isSelecting || onBackClick != null) {
                 TopbarNavigationButton(
                     onClick = { if (isSelecting) onClearSelection() else onBackClick?.invoke() },
-                    imageVector = if (isSelecting) Icons.Default.Close else Icons.AutoMirrored.Filled.ArrowBack,
+                    imageVector = if (isSelecting) AppIcons.Close else AppIcons.Back,
                     contentDescription = if (isSelecting) "取消选择" else "返回"
                 )
             }
@@ -113,7 +111,6 @@ fun <T> DynamicTopAppBar(
                     query = state.searchKey,
                     onQueryChange = onSearchQueryChange,
                     placeholder = searchPlaceholder,
-                    leadingIcon = { Icon(searchLeadingIcon, null) },
                     trailingIcon = searchTrailingIcon,
                     dropdownMenu = searchDropdownMenu
                 )

+ 1 - 1
app/src/main/java/io/legado/app/ui/widget/components/topbar/GlassMediumFlexibleTopAppBar.kt

@@ -21,8 +21,8 @@ import io.legado.app.ui.theme.LegadoTheme
 import io.legado.app.ui.theme.LocalHazeState
 import io.legado.app.ui.theme.ThemeResolver
 import io.legado.app.ui.theme.responsiveHazeEffect
-import io.legado.app.ui.widget.components.AdaptiveAnimatedText
 import io.legado.app.ui.widget.components.GlassDefaults
+import io.legado.app.ui.widget.components.text.AdaptiveAnimatedText
 import top.yukonga.miuix.kmp.basic.MiuixScrollBehavior
 import top.yukonga.miuix.kmp.theme.MiuixTheme
 import top.yukonga.miuix.kmp.basic.TopAppBar as MiuixTopAppBar