diff --git a/tools/idea-plugin/CHANGELOG.md b/tools/idea-plugin/CHANGELOG.md index b3a110a18..499071867 100644 --- a/tools/idea-plugin/CHANGELOG.md +++ b/tools/idea-plugin/CHANGELOG.md @@ -5,10 +5,12 @@ ### Added - Add export as file action for `Simple mode` and `ImageVector to XML` tool -- Add validation for exact duplicate icon names (e.g., `test-icon.svg` and `test_icon.svg` both produce `TestIcon.kt`) -- Add validation for case-insensitive duplicate icon names to prevent file overwrites on macOS/Windows -- Add automatic re-validation when `useFlatPackage` setting changes to detect new conflicts immediately +- [IconPack] Add validation for exact duplicate icon names (e.g., `test-icon.svg` and `test_icon.svg` both produce + `TestIcon.kt`) +- [IconPack] Add validation for case-insensitive duplicate icon names to prevent file overwrites on macOS/Windows +- [IconPack] Add automatic re-validation when `useFlatPackage` setting changes to detect new conflicts immediately - [Web Import] Add `Bootstrap` icons provider +- [Web Import] Add persistent settings for font customization for all providers - [Gutter] Add support for multiple icons in kt file ### Fixed diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/PersistentSettings.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/PersistentSettings.kt index 04bc6df92..654f19b80 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/PersistentSettings.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/service/PersistentSettings.kt @@ -10,6 +10,11 @@ import io.github.composegears.valkyrie.jewel.tooling.GlobalPreviewState import io.github.composegears.valkyrie.sdk.shared.ValkyrieMode import io.github.composegears.valkyrie.service.PersistentSettings.ValkyrieState import io.github.composegears.valkyrie.ui.domain.model.PreviewType +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings.Companion.DEFAULT_FILL +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings.Companion.DEFAULT_GRADE +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings.Companion.DEFAULT_OPTICAL_SIZE +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings.Companion.DEFAULT_WEIGHT +import io.github.composegears.valkyrie.ui.screen.webimport.standard.model.SizeSettings.Companion.DEFAULT_SIZE @State(name = "Valkyrie.Settings", storages = [Storage("valkyrie_settings.xml")]) class PersistentSettings : SimplePersistentStateComponent(ValkyrieState()) { @@ -50,6 +55,18 @@ class PersistentSettings : SimplePersistentStateComponent(Valkyri var showImageVectorPreview: Boolean by property(true) var showIconsInProjectView: Boolean by property(true) var indentSize: Int by property(4) + + // MaterialSymbols + var materialFontFill: Boolean by property(DEFAULT_FILL) + var materialFontWeight: Int by property(DEFAULT_WEIGHT) + var materialFontGrade: Int by property(DEFAULT_GRADE) + var materialFontOpticalSize: Float by property(DEFAULT_OPTICAL_SIZE) + + // Lucide + var lucideSize: Int by property(DEFAULT_SIZE) + + // Bootstrap + var bootstrapSize: Int by property(DEFAULT_SIZE) } companion object { diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/InMemorySettings.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/InMemorySettings.kt index e072e870b..c322f9a3c 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/InMemorySettings.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/settings/InMemorySettings.kt @@ -9,6 +9,11 @@ import io.github.composegears.valkyrie.service.PersistentSettings.Companion.pers import io.github.composegears.valkyrie.ui.domain.model.PreviewType import io.github.composegears.valkyrie.ui.extension.or import io.github.composegears.valkyrie.ui.extension.updateState +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings.Companion.DEFAULT_FILL +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings.Companion.DEFAULT_GRADE +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings.Companion.DEFAULT_OPTICAL_SIZE +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings.Companion.DEFAULT_WEIGHT +import io.github.composegears.valkyrie.ui.screen.webimport.standard.model.SizeSettings.Companion.DEFAULT_SIZE import java.util.Collections.emptyList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -36,6 +41,8 @@ class InMemorySettings(project: Project) { _settings.updateState { persistentSettings.state.toValkyriesSettings() } } + fun readState(selector: PersistentSettings.ValkyrieState.() -> T): T = selector(persistentSettings.state) + fun clear() = update { mode = ValkyrieMode.Unspecified previewType = PreviewType.Pixel @@ -54,6 +61,12 @@ class InMemorySettings(project: Project) { showImageVectorPreview = true showIconsInProjectView = true indentSize = 4 + materialFontFill = DEFAULT_FILL + materialFontWeight = DEFAULT_WEIGHT + materialFontGrade = DEFAULT_GRADE + materialFontOpticalSize = DEFAULT_OPTICAL_SIZE + lucideSize = DEFAULT_SIZE + bootstrapSize = DEFAULT_SIZE } fun updateUIState(uiState: SavedState) { diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/ui/SidePanel.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/ui/SidePanel.kt index 374a459f1..a8f8ebe66 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/ui/SidePanel.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/common/ui/SidePanel.kt @@ -20,8 +20,8 @@ import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.unit.dp import io.github.composegears.valkyrie.jewel.tooling.PreviewTheme import io.github.composegears.valkyrie.sdk.compose.foundation.rememberMutableState -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.FontSettings -import io.github.composegears.valkyrie.ui.screen.webimport.material.ui.FontCustomization +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings +import io.github.composegears.valkyrie.ui.screen.webimport.material.ui.MaterialFontCustomization import org.jetbrains.compose.ui.tooling.preview.Preview import org.jetbrains.jewel.foundation.theme.JewelTheme import org.jetbrains.jewel.ui.component.DefaultButton @@ -81,9 +81,9 @@ private fun SidePanelPreview() = PreviewTheme { isOpen = isOpen, onClose = { isOpen = false }, content = { - FontCustomization( + MaterialFontCustomization( modifier = Modifier.fillMaxHeight(), - fontSettings = FontSettings(fill = true), + fontSettings = MaterialFontSettings(fill = true), onClose = { isOpen = false }, onSettingsChange = { }, ) diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/MaterialSymbolsImportScreen.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/MaterialSymbolsImportScreen.kt index 6fb25a930..da4ab5f5e 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/MaterialSymbolsImportScreen.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/MaterialSymbolsImportScreen.kt @@ -58,9 +58,9 @@ import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.IconLoading import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.SidePanel import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.Category import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.IconModel -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.FontSettings -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.IconFontFamily -import io.github.composegears.valkyrie.ui.screen.webimport.material.ui.FontCustomization +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialIconFontFamily +import io.github.composegears.valkyrie.ui.screen.webimport.material.ui.MaterialFontCustomization import io.github.composegears.valkyrie.ui.screen.webimport.material.ui.MaterialTopActions import io.github.composegears.valkyrie.util.stringResource import kotlinx.coroutines.launch @@ -103,9 +103,9 @@ private fun MaterialSymbolsImportUI( state: MaterialState, onBack: () -> Unit, onSelectIcon: (IconModel) -> Unit, - onSelectFontFamily: (IconFontFamily) -> Unit, + onSelectFontFamily: (MaterialIconFontFamily) -> Unit, onSelectCategory: (Category) -> Unit, - onSettingsChange: (FontSettings) -> Unit, + onSettingsChange: (MaterialFontSettings) -> Unit, onSearchQueryChange: (String) -> Unit, ) { var isSidePanelOpen by rememberSaveable { mutableStateOf(false) } @@ -156,7 +156,7 @@ private fun MaterialSymbolsImportUI( isOpen = isSidePanelOpen, onClose = { isSidePanelOpen = false }, content = { - FontCustomization( + MaterialFontCustomization( fontSettings = current.fontSettings, onClose = { isSidePanelOpen = false }, onSettingsChange = onSettingsChange, @@ -174,7 +174,7 @@ private fun MaterialSymbolsImportUI( private fun IconsContent( state: MaterialState.Success, onSelectIcon: (IconModel) -> Unit, - onSelectFontFamily: (IconFontFamily) -> Unit, + onSelectFontFamily: (MaterialIconFontFamily) -> Unit, onSelectCategory: (Category) -> Unit, onToggleSidePanel: () -> Unit, onSearchQueryChange: (String) -> Unit, diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/MaterialSymbolsViewModel.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/MaterialSymbolsViewModel.kt index f8217ff28..d6f896afb 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/MaterialSymbolsViewModel.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/MaterialSymbolsViewModel.kt @@ -9,6 +9,7 @@ import com.composegears.tiamat.navigation.asStateFlow import com.composegears.tiamat.navigation.recordOf import io.github.composegears.valkyrie.parser.unified.util.IconNameFormatter import io.github.composegears.valkyrie.sdk.core.extensions.safeAs +import io.github.composegears.valkyrie.ui.di.DI import io.github.composegears.valkyrie.ui.screen.webimport.common.model.FontByteArray import io.github.composegears.valkyrie.ui.screen.webimport.common.model.GridItem import io.github.composegears.valkyrie.ui.screen.webimport.common.util.filterGridItems @@ -17,8 +18,8 @@ import io.github.composegears.valkyrie.ui.screen.webimport.material.di.MaterialS import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.Category import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.IconModel import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.MaterialConfig -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.FontSettings -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.IconFontFamily +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialIconFontFamily import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.channels.Channel @@ -27,8 +28,9 @@ import kotlinx.coroutines.launch class MaterialSymbolsViewModel(savedState: MutableSavedState) : ViewModel() { - private val fontCache = mutableMapOf() + private val fontCache = mutableMapOf() private val materialSymbolsConfigUseCase = inject(MaterialSymbolsModule.materialSymbolsConfigUseCase) + private val inMemorySettings = inject(DI.core.inMemorySettings) private val materialRecord = savedState.recordOf( key = "materialSymbols", @@ -39,7 +41,7 @@ class MaterialSymbolsViewModel(savedState: MutableSavedState) : ViewModel() { private val _events = Channel() val events = _events.receiveAsFlow() - private var currentFontFamily: IconFontFamily = IconFontFamily.OUTLINED + private var currentFontFamily: MaterialIconFontFamily = MaterialIconFontFamily.Outlined private var iconLoadJob: Job? = null init { @@ -56,19 +58,28 @@ class MaterialSymbolsViewModel(savedState: MutableSavedState) : ViewModel() { materialRecord.value = MaterialState.Loading runCatching { + val initialSettings = inMemorySettings.readState { + MaterialFontSettings( + fill = materialFontFill, + weight = materialFontWeight, + grade = materialFontGrade, + opticalSize = materialFontOpticalSize, + ) + } val config = materialSymbolsConfigUseCase.loadConfig() materialRecord.value = MaterialState.Success( config = config, gridItems = config.gridItems.toGridItems(), + fontSettings = initialSettings, ) - downloadFont(IconFontFamily.OUTLINED) + downloadFont(MaterialIconFontFamily.Outlined) }.onFailure { materialRecord.value = MaterialState.Error("Error loading Material Symbols config: ${it.message}") } } } - fun downloadFont(iconFontFamily: IconFontFamily) { + fun downloadFont(iconFontFamily: MaterialIconFontFamily) { currentFontFamily = iconFontFamily viewModelScope.launch { val cachedFont = fontCache[iconFontFamily] @@ -166,8 +177,14 @@ class MaterialSymbolsViewModel(savedState: MutableSavedState) : ViewModel() { searchQuery = searchQuery, ) - fun updateFontSettings(fontSettings: FontSettings) { + fun updateFontSettings(fontSettings: MaterialFontSettings) { viewModelScope.launch { + inMemorySettings.update { + materialFontFill = fontSettings.fill + materialFontWeight = fontSettings.weight + materialFontGrade = fontSettings.grade + materialFontOpticalSize = fontSettings.opticalSize + } updateSuccess { state -> state.copy(fontSettings = fontSettings) } @@ -198,8 +215,8 @@ sealed interface MaterialState { val config: MaterialConfig, val gridItems: List = emptyList(), val selectedCategory: Category = Category.All, - val fontSettings: FontSettings = FontSettings(), - val iconFontFamily: IconFontFamily = IconFontFamily.OUTLINED, + val fontSettings: MaterialFontSettings, + val iconFontFamily: MaterialIconFontFamily = MaterialIconFontFamily.Outlined, val fontByteArray: FontByteArray? = null, ) : MaterialState diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/data/font/MaterialFontRepository.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/data/font/MaterialFontRepository.kt index e66b05961..8b3ade21e 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/data/font/MaterialFontRepository.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/data/font/MaterialFontRepository.kt @@ -1,6 +1,6 @@ package io.github.composegears.valkyrie.ui.screen.webimport.material.data.font -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.FontSettings +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings import io.ktor.client.HttpClient import io.ktor.client.request.get import io.ktor.client.statement.bodyAsChannel @@ -20,7 +20,7 @@ class MaterialFontRepository( suspend fun downloadSvg( name: String, fontFamily: String, - fontSettings: FontSettings, + fontSettings: MaterialFontSettings, ): String = withContext(Dispatchers.IO) { val iconOption = fontSettings.iconOption() val iconSize = fontSettings.iconSize() @@ -29,7 +29,7 @@ class MaterialFontRepository( httpClient.get(url).bodyAsText() } - private fun FontSettings.iconOption(): String { + private fun MaterialFontSettings.iconOption(): String { if (!isModified) { return "default" } @@ -50,5 +50,5 @@ class MaterialFontRepository( } } - private fun FontSettings.iconSize() = "${opticalSize.toInt()}px" + private fun MaterialFontSettings.iconSize() = "${opticalSize.toInt()}px" } diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/MaterialSymbolsConfigUseCase.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/MaterialSymbolsConfigUseCase.kt index 79794c2f4..726f8f9f2 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/MaterialSymbolsConfigUseCase.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/MaterialSymbolsConfigUseCase.kt @@ -7,8 +7,8 @@ import io.github.composegears.valkyrie.ui.screen.webimport.material.data.font.Ma import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.Category import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.IconModel import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.MaterialConfig -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.FontSettings -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.IconFontFamily +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialIconFontFamily class MaterialSymbolsConfigUseCase( private val configRepository: MaterialSymbolsConfigRepository, @@ -45,7 +45,7 @@ class MaterialSymbolsConfigUseCase( ) } - suspend fun loadFont(iconFontFamily: IconFontFamily): FontByteArray { + suspend fun loadFont(iconFontFamily: MaterialIconFontFamily): FontByteArray { val bytes = fontRepository.downloadFont(iconFontFamily.cdnUrl) return FontByteArray(bytes = bytes) @@ -54,7 +54,7 @@ class MaterialSymbolsConfigUseCase( suspend fun loadIcon( name: String, fontFamily: String, - fontSettings: FontSettings, + fontSettings: MaterialFontSettings, ): String { return fontRepository.downloadSvg( name = name, diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/FontSettings.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/FontSettings.kt deleted file mode 100644 index 40624556b..000000000 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/FontSettings.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font - -import io.github.composegears.valkyrie.ui.screen.webimport.common.model.IconSettings - -data class FontSettings( - val fill: Boolean = false, - val weight: Int = 400, - val grade: Int = 0, - val opticalSize: Float = 24f, - val iconFontFamily: IconFontFamily = IconFontFamily.OUTLINED, -) : IconSettings { - init { - require(weight in 100..700) { "Weight must be between 100 and 700" } - require(grade in -25..200) { "Grade must be between -25 and 200" } - require(opticalSize in 20f..48f) { "Optical size must be between 20 and 48" } - } - - override val isModified: Boolean - get() = fill || weight != 400 || grade != 0 || opticalSize != 24f -} diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/MaterialFontSettings.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/MaterialFontSettings.kt new file mode 100644 index 000000000..b1afedd40 --- /dev/null +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/MaterialFontSettings.kt @@ -0,0 +1,27 @@ +package io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font + +import io.github.composegears.valkyrie.ui.screen.webimport.common.model.IconSettings + +data class MaterialFontSettings( + val fill: Boolean = DEFAULT_FILL, + val weight: Int = DEFAULT_WEIGHT, + val grade: Int = DEFAULT_GRADE, + val opticalSize: Float = DEFAULT_OPTICAL_SIZE, +) : IconSettings { + + companion object { + const val DEFAULT_FILL = false + const val DEFAULT_WEIGHT = 400 + const val DEFAULT_GRADE = 0 + const val DEFAULT_OPTICAL_SIZE = 24f + } + + init { + require(weight in 100..700) { "Weight must be between 100 and 700" } + require(grade in -25..200) { "Grade must be between -25 and 200" } + require(opticalSize in 20f..48f) { "Optical size must be between 20 and 48" } + } + + override val isModified: Boolean + get() = fill || weight != DEFAULT_WEIGHT || grade != DEFAULT_GRADE || opticalSize != DEFAULT_OPTICAL_SIZE +} diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/IconFontFamily.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/MaterialIconFontFamily.kt similarity index 61% rename from tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/IconFontFamily.kt rename to tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/MaterialIconFontFamily.kt index 092991d2c..6527364ec 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/IconFontFamily.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/domain/model/font/MaterialIconFontFamily.kt @@ -1,25 +1,21 @@ package io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font -enum class IconFontFamily( - val githubUrl: String, +enum class MaterialIconFontFamily( val cdnUrl: String, val displayName: String, val fontFamily: String, ) { - OUTLINED( - githubUrl = "https://github.com/google/material-design-icons/raw/refs/heads/master/variablefont/MaterialSymbolsOutlined%5BFILL,GRAD,opsz,wght%5D.ttf", + Outlined( cdnUrl = "https://cdn.jsdelivr.net/gh/google/material-design-icons@master/variablefont/MaterialSymbolsOutlined%5BFILL%2CGRAD%2Copsz%2Cwght%5D.ttf", displayName = "Outlined", fontFamily = "materialsymbolsoutlined", ), - ROUNDED( - githubUrl = "https://github.com/google/material-design-icons/raw/refs/heads/master/variablefont/MaterialSymbolsRounded%5BFILL,GRAD,opsz,wght%5D.ttf", + Rounded( cdnUrl = "https://cdn.jsdelivr.net/gh/google/material-design-icons@master/variablefont/MaterialSymbolsRounded%5BFILL%2CGRAD%2Copsz%2Cwght%5D.ttf", displayName = "Rounded", fontFamily = "materialsymbolsrounded", ), - SHARP( - githubUrl = "https://github.com/google/material-design-icons/raw/refs/heads/master/variablefont/MaterialSymbolsSharp%5BFILL,GRAD,opsz,wght%5D.ttf", + Sharp( cdnUrl = "https://cdn.jsdelivr.net/gh/google/material-design-icons@master/variablefont/MaterialSymbolsSharp%5BFILL%2CGRAD%2Copsz%2Cwght%5D.ttf", displayName = "Sharp", fontFamily = "materialsymbolssharp", diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/FontCustomization.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialFontCustomization.kt similarity index 92% rename from tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/FontCustomization.kt rename to tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialFontCustomization.kt index 44575a1ec..684249974 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/FontCustomization.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialFontCustomization.kt @@ -21,7 +21,7 @@ import io.github.composegears.valkyrie.sdk.compose.foundation.layout.CenterVerti import io.github.composegears.valkyrie.sdk.compose.foundation.layout.Spacer import io.github.composegears.valkyrie.sdk.compose.foundation.rememberMutableState import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.CustomizationToolbar -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.FontSettings +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialFontSettings import io.github.composegears.valkyrie.util.ValkyrieBundle.message import io.github.composegears.valkyrie.util.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview @@ -39,16 +39,16 @@ private const val GRADE_HELP_URL = "$BASE_URL#3ad55207-1cb0-43af-8092-fad2762f69 private const val OPTICAL_SIZE_HELP_URL = "$BASE_URL#b41cbc01-9b49-4a44-a525-d153d1ea1425" @Composable -fun FontCustomization( - fontSettings: FontSettings, - onSettingsChange: (FontSettings) -> Unit, +fun MaterialFontCustomization( + fontSettings: MaterialFontSettings, + onSettingsChange: (MaterialFontSettings) -> Unit, onClose: () -> Unit, modifier: Modifier = Modifier, ) { Column(modifier = modifier) { CustomizationToolbar( onClose = onClose, - onReset = { onSettingsChange(FontSettings()) }, + onReset = { onSettingsChange(MaterialFontSettings()) }, isModified = fontSettings.isModified, ) HorizontalDivider(color = LocalGroupHeaderStyle.current.colors.divider) @@ -61,8 +61,8 @@ fun FontCustomization( @Composable private fun FontPlayground( - fontSettings: FontSettings, - onSettingsChange: (FontSettings) -> Unit, + fontSettings: MaterialFontSettings, + onSettingsChange: (MaterialFontSettings) -> Unit, ) { val dropdownWidth = 80.dp @@ -148,10 +148,10 @@ private fun FontPlayground( @Preview @Composable -private fun FontCustomizationPreview() = PreviewTheme(alignment = Alignment.TopEnd) { - var settings by rememberMutableState { FontSettings(fill = true) } +private fun MaterialFontCustomizationPreview() = PreviewTheme(alignment = Alignment.TopEnd) { + var settings by rememberMutableState { MaterialFontSettings(fill = true) } - FontCustomization( + MaterialFontCustomization( modifier = Modifier .width(300.dp) .fillMaxHeight() diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/FontFamilyDropdown.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialFontFamilyDropdown.kt similarity index 71% rename from tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/FontFamilyDropdown.kt rename to tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialFontFamilyDropdown.kt index 736d6c605..ce89a4b10 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/FontFamilyDropdown.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialFontFamilyDropdown.kt @@ -10,18 +10,18 @@ import androidx.compose.ui.unit.dp import io.github.composegears.valkyrie.jewel.DropdownList import io.github.composegears.valkyrie.jewel.tooling.PreviewTheme import io.github.composegears.valkyrie.sdk.compose.foundation.rememberMutableState -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.IconFontFamily +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialIconFontFamily import org.jetbrains.compose.ui.tooling.preview.Preview @Composable -fun FontFamilyDropdown( - fontFamily: IconFontFamily, +fun MaterialFontFamilyDropdown( + fontFamily: MaterialIconFontFamily, modifier: Modifier = Modifier, - onSelectFontFamily: (IconFontFamily) -> Unit, + onSelectFontFamily: (MaterialIconFontFamily) -> Unit, ) { DropdownList( modifier = modifier, - items = IconFontFamily.entries, + items = MaterialIconFontFamily.entries, selected = fontFamily, transform = { it.displayName }, onSelectItem = onSelectFontFamily, @@ -30,10 +30,10 @@ fun FontFamilyDropdown( @Preview @Composable -private fun FontFamilyDropdownPreview() = PreviewTheme(alignment = Alignment.Center) { - var fontFamily by rememberMutableState { IconFontFamily.OUTLINED } +private fun MaterialFontFamilyDropdownPreview() = PreviewTheme(alignment = Alignment.Center) { + var fontFamily by rememberMutableState { MaterialIconFontFamily.Outlined } - FontFamilyDropdown( + MaterialFontFamilyDropdown( modifier = Modifier.width(100.dp), fontFamily = fontFamily, onSelectFontFamily = { fontFamily = it }, diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialTopActions.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialTopActions.kt index 13d4af9ae..a75e46601 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialTopActions.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/material/ui/MaterialTopActions.kt @@ -11,16 +11,16 @@ import io.github.composegears.valkyrie.jewel.tooling.PreviewTheme import io.github.composegears.valkyrie.sdk.compose.foundation.rememberMutableState import io.github.composegears.valkyrie.ui.screen.webimport.common.ui.WebImportTopActions import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.Category -import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.IconFontFamily +import io.github.composegears.valkyrie.ui.screen.webimport.material.domain.model.font.MaterialIconFontFamily import org.jetbrains.compose.ui.tooling.preview.Preview @Composable fun MaterialTopActions( categories: List, selectedCategory: Category, - iconFontFamily: IconFontFamily, + iconFontFamily: MaterialIconFontFamily, onToggleSidePanel: () -> Unit, - onSelectFontFamily: (IconFontFamily) -> Unit, + onSelectFontFamily: (MaterialIconFontFamily) -> Unit, onSelectCategory: (Category) -> Unit, onSearchQueryChange: (String) -> Unit, modifier: Modifier = Modifier, @@ -30,7 +30,7 @@ fun MaterialTopActions( onToggleCustomization = onToggleSidePanel, onSearchQueryChange = onSearchQueryChange, actionsContent = { - FontFamilyDropdown( + MaterialFontFamilyDropdown( modifier = Modifier.width(100.dp), fontFamily = iconFontFamily, onSelectFontFamily = onSelectFontFamily, @@ -51,7 +51,7 @@ fun MaterialTopActions( @Composable private fun MaterialTopActionsPreview() = PreviewTheme { var category by rememberMutableState { Category("Action") } - var fontFamily by rememberMutableState { IconFontFamily.OUTLINED } + var fontFamily by rememberMutableState { MaterialIconFontFamily.Outlined } MaterialTopActions( selectedCategory = category, diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/StandardIconViewModel.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/StandardIconViewModel.kt index 4cb152feb..3d9e6d88b 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/StandardIconViewModel.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/StandardIconViewModel.kt @@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope import com.composegears.tiamat.navigation.MutableSavedState import com.composegears.tiamat.navigation.asStateFlow import com.composegears.tiamat.navigation.recordOf -import com.intellij.openapi.diagnostic.Logger import io.github.composegears.valkyrie.parser.unified.util.IconNameFormatter import io.github.composegears.valkyrie.sdk.core.extensions.safeAs import io.github.composegears.valkyrie.ui.screen.webimport.common.model.FontByteArray @@ -43,8 +42,6 @@ class StandardIconViewModel( private var downloadJob: Job? = null private var fontLoadJob: Job? = null - private val log = Logger.getInstance("StandardIconViewModel-${provider.providerName}") - init { when (val initialState = stateRecord.value) { is StandardState.Success if initialState.fontByteArray == null -> downloadFont() @@ -69,10 +66,10 @@ class StandardIconViewModel( stateRecord.value = StandardState.Success( config = config, gridItems = config.gridItems.toGridItems(), + settings = SizeSettings(size = provider.persistentSize), ) downloadFont() }.onFailure { error -> - log.error("Error loading ${provider.providerName} icons", error) stateRecord.value = StandardState.Error( "Error loading ${provider.providerName} icons: ${error.message}", ) @@ -90,8 +87,6 @@ class StandardIconViewModel( val bytes = provider.loadFontBytes() fontCache = bytes updateSuccess { it.copy(fontByteArray = bytes) } - }.onFailure { error -> - log.error("Error loading ${provider.providerName} font", error) } } else { updateSuccess { it.copy(fontByteArray = cachedFont) } @@ -113,8 +108,6 @@ class StandardIconViewModel( name = IconNameFormatter.format(icon.displayName), ), ) - }.onFailure { error -> - log.error("Failed to download icon '${icon.name}'", error) } } } @@ -145,6 +138,7 @@ class StandardIconViewModel( fun updateSettings(settings: SizeSettings) { viewModelScope.launch(Dispatchers.Default) { + provider.updatePersistentSize(settings.size) updateSuccess { state -> state.copy(settings = settings) } diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/bootstrap/di/BootstrapModule.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/bootstrap/di/BootstrapModule.kt index 076958111..e9e3ed73b 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/bootstrap/di/BootstrapModule.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/bootstrap/di/BootstrapModule.kt @@ -1,12 +1,14 @@ package io.github.composegears.valkyrie.ui.screen.webimport.standard.bootstrap.di import com.composegears.leviathan.Leviathan +import io.github.composegears.valkyrie.ui.di.coreModule import io.github.composegears.valkyrie.ui.screen.webimport.common.di.NetworkModule import io.github.composegears.valkyrie.ui.screen.webimport.standard.bootstrap.data.BootstrapRepository import io.github.composegears.valkyrie.ui.screen.webimport.standard.bootstrap.domain.BootstrapUseCase object BootstrapModule : Leviathan() { private val network = NetworkModule + private val coreModule = coreModule() private val bootstrapRepository by instanceOf { BootstrapRepository( @@ -18,6 +20,7 @@ object BootstrapModule : Leviathan() { val bootstrapUseCase by instanceOf { BootstrapUseCase( repository = inject(bootstrapRepository), + inMemorySettings = inject(coreModule.inMemorySettings), ) } } diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/bootstrap/domain/BootstrapUseCase.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/bootstrap/domain/BootstrapUseCase.kt index 9ff6ad794..6aa9fb81e 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/bootstrap/domain/BootstrapUseCase.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/bootstrap/domain/BootstrapUseCase.kt @@ -1,5 +1,6 @@ package io.github.composegears.valkyrie.ui.screen.webimport.standard.bootstrap.domain +import io.github.composegears.valkyrie.settings.InMemorySettings import io.github.composegears.valkyrie.ui.screen.webimport.common.model.FontByteArray import io.github.composegears.valkyrie.ui.screen.webimport.standard.bootstrap.data.BootstrapRepository import io.github.composegears.valkyrie.ui.screen.webimport.standard.domain.StandardIconProvider @@ -13,11 +14,19 @@ import io.github.composegears.valkyrie.ui.screen.webimport.standard.model.toStan class BootstrapUseCase( private val repository: BootstrapRepository, + private val inMemorySettings: InMemorySettings, ) : StandardIconProvider { override val providerName: String = "Bootstrap" override val stateKey: String = "bootstrap" override val fontAlias: String = "bootstrap-icons" + override val persistentSize: Int = inMemorySettings.readState { bootstrapSize } + + override fun updatePersistentSize(value: Int) { + inMemorySettings.update { + bootstrapSize = value + } + } override suspend fun loadConfig(): StandardIconConfig { val codepoints = repository.loadIconList() diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/domain/StandardIconProvider.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/domain/StandardIconProvider.kt index 9dcb68ad3..e689c6feb 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/domain/StandardIconProvider.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/domain/StandardIconProvider.kt @@ -8,6 +8,9 @@ interface StandardIconProvider { val providerName: String val stateKey: String val fontAlias: String + val persistentSize: Int + + fun updatePersistentSize(value: Int) suspend fun loadConfig(): StandardIconConfig suspend fun loadFontBytes(): FontByteArray diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/lucide/di/LucideModule.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/lucide/di/LucideModule.kt index 9e2ac7a88..1808647b1 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/lucide/di/LucideModule.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/lucide/di/LucideModule.kt @@ -1,12 +1,14 @@ package io.github.composegears.valkyrie.ui.screen.webimport.standard.lucide.di import com.composegears.leviathan.Leviathan +import io.github.composegears.valkyrie.ui.di.coreModule import io.github.composegears.valkyrie.ui.screen.webimport.common.di.NetworkModule import io.github.composegears.valkyrie.ui.screen.webimport.standard.lucide.data.LucideRepository import io.github.composegears.valkyrie.ui.screen.webimport.standard.lucide.domain.LucideUseCase object LucideModule : Leviathan() { private val network = NetworkModule + private val core = coreModule() private val lucideRepository by instanceOf { LucideRepository( @@ -18,6 +20,7 @@ object LucideModule : Leviathan() { val lucideUseCase by instanceOf { LucideUseCase( repository = inject(lucideRepository), + inMemorySettings = inject(core.inMemorySettings), ) } } diff --git a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/lucide/domain/LucideUseCase.kt b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/lucide/domain/LucideUseCase.kt index c7e510c4d..e57ccf4d0 100644 --- a/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/lucide/domain/LucideUseCase.kt +++ b/tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/webimport/standard/lucide/domain/LucideUseCase.kt @@ -1,5 +1,6 @@ package io.github.composegears.valkyrie.ui.screen.webimport.standard.lucide.domain +import io.github.composegears.valkyrie.settings.InMemorySettings import io.github.composegears.valkyrie.ui.screen.webimport.common.model.FontByteArray import io.github.composegears.valkyrie.ui.screen.webimport.standard.domain.StandardIconProvider import io.github.composegears.valkyrie.ui.screen.webimport.standard.domain.SvgSizeCustomizer @@ -13,11 +14,19 @@ import io.github.composegears.valkyrie.ui.screen.webimport.standard.model.toStan class LucideUseCase( private val repository: LucideRepository, + private val inMemorySettings: InMemorySettings, ) : StandardIconProvider { override val providerName: String = "Lucide" override val stateKey: String = "lucide" override val fontAlias: String = "lucide" + override val persistentSize: Int = inMemorySettings.readState { lucideSize } + + override fun updatePersistentSize(value: Int) { + inMemorySettings.update { + lucideSize = value + } + } override suspend fun loadConfig(): StandardIconConfig { val iconMetadataList = repository.loadIconList()