Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions tools/idea-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>(ValkyrieState()) {
Expand Down Expand Up @@ -50,6 +55,18 @@ class PersistentSettings : SimplePersistentStateComponent<ValkyrieState>(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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -36,6 +41,8 @@ class InMemorySettings(project: Project) {
_settings.updateState { persistentSettings.state.toValkyriesSettings() }
}

fun <T> readState(selector: PersistentSettings.ValkyrieState.() -> T): T = selector(persistentSettings.state)

fun clear() = update {
mode = ValkyrieMode.Unspecified
previewType = PreviewType.Pixel
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 = { },
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) }
Expand Down Expand Up @@ -156,7 +156,7 @@ private fun MaterialSymbolsImportUI(
isOpen = isSidePanelOpen,
onClose = { isSidePanelOpen = false },
content = {
FontCustomization(
MaterialFontCustomization(
fontSettings = current.fontSettings,
onClose = { isSidePanelOpen = false },
onSettingsChange = onSettingsChange,
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -27,8 +28,9 @@ import kotlinx.coroutines.launch

class MaterialSymbolsViewModel(savedState: MutableSavedState) : ViewModel() {

private val fontCache = mutableMapOf<IconFontFamily, FontByteArray>()
private val fontCache = mutableMapOf<MaterialIconFontFamily, FontByteArray>()
private val materialSymbolsConfigUseCase = inject(MaterialSymbolsModule.materialSymbolsConfigUseCase)
private val inMemorySettings = inject(DI.core.inMemorySettings)

private val materialRecord = savedState.recordOf<MaterialState>(
key = "materialSymbols",
Expand All @@ -39,7 +41,7 @@ class MaterialSymbolsViewModel(savedState: MutableSavedState) : ViewModel() {
private val _events = Channel<MaterialEvent>()
val events = _events.receiveAsFlow()

private var currentFontFamily: IconFontFamily = IconFontFamily.OUTLINED
private var currentFontFamily: MaterialIconFontFamily = MaterialIconFontFamily.Outlined
private var iconLoadJob: Job? = null

init {
Expand All @@ -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]
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -198,8 +215,8 @@ sealed interface MaterialState {
val config: MaterialConfig,
val gridItems: List<GridItem> = 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

Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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()
Expand All @@ -29,7 +29,7 @@ class MaterialFontRepository(
httpClient.get(url).bodyAsText()
}

private fun FontSettings.iconOption(): String {
private fun MaterialFontSettings.iconOption(): String {
if (!isModified) {
return "default"
}
Expand All @@ -50,5 +50,5 @@ class MaterialFontRepository(
}
}

private fun FontSettings.iconSize() = "${opticalSize.toInt()}px"
private fun MaterialFontSettings.iconSize() = "${opticalSize.toInt()}px"
}
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -54,7 +54,7 @@ class MaterialSymbolsConfigUseCase(
suspend fun loadIcon(
name: String,
fontFamily: String,
fontSettings: FontSettings,
fontSettings: MaterialFontSettings,
): String {
return fontRepository.downloadSvg(
name = name,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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
}
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
Loading