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
5 changes: 5 additions & 0 deletions tools/idea-plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
- Add "Suppress unused receiver warning" setting to generate `@Suppress("UnusedReceiverParameter")` annotation on
ImageVector extension properties when an icon pack receiver is used (e.g. `ValkyrieIcons`)

### Changed

- [IconPack] Don’t override the `.kt` file object if the content hasn’t changed

### Fixed

- [Gutter] Fixed rendering of icons with gradients and Compose colors
- [Gutter] Fixed rendering of icons with fully qualified imports
- [IconPack] Fix incorrect initial state of `packageName` field in new pack mode when destination folder is selected
- [IconPack] The IDE didn’t automatically display the new icon pack object file

## 1.5.0 - 2026-04-17

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package io.github.composegears.valkyrie.ui.screen.mode.iconpack.common.util

import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.openapi.command.writeCommandAction
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VfsUtil
import io.github.composegears.valkyrie.generator.core.IconPack
import io.github.composegears.valkyrie.generator.iconpack.IconPackGenerator
import io.github.composegears.valkyrie.generator.iconpack.IconPackGeneratorConfig
import io.github.composegears.valkyrie.sdk.core.extensions.writeToKt
import io.github.composegears.valkyrie.sdk.shared.ValkyrieMode
import io.github.composegears.valkyrie.settings.InMemorySettings
import io.github.composegears.valkyrie.settings.updateNestedPack
import io.github.composegears.valkyrie.ui.screen.mode.iconpack.common.model.InputFieldState

object IconPackWriter {

fun savePack(
suspend fun savePack(
project: Project,
inMemorySettings: InMemorySettings,
inputFieldState: InputFieldState,
writeToFile: Boolean = true,
Expand Down Expand Up @@ -40,12 +43,37 @@ object IconPackWriter {
license = inputFieldState.license.text.ifEmpty { null },
),
)
iconPack.content.writeToKt(
outputDir = currentSettings.iconPackDestination,
nameWithoutExtension = iconPack.name,

val fileName = "${iconPack.name}.kt"
val content = iconPack.content

writeTextFile(
project = project,
directoryPath = currentSettings.iconPackDestination,
fileName = fileName,
content = content,
)
}
}

VirtualFileManager.getInstance().asyncRefresh()
private suspend fun writeTextFile(
project: Project,
directoryPath: String,
fileName: String,
content: String,
) {
writeCommandAction(
project = project,
commandName = "Generate Icon Pack",
) {
runCatching {
val dir = VfsUtil.createDirectoryIfMissing(directoryPath) ?: return@writeCommandAction
val file = dir.findChild(fileName) ?: dir.createChildData(IconPackWriter, fileName)

VfsUtil.saveText(file, content)
}.getOrElse {
Logger.getInstance(IconPackWriter::class.java).error("Failed to save iconpack: ${it.message}")
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,13 @@ import io.github.composegears.valkyrie.ui.screen.mode.iconpack.existingpack.mode
import io.github.composegears.valkyrie.util.extension.resolveKtFile
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class ExistingPackViewModel : ViewModel() {

Expand Down Expand Up @@ -71,7 +69,7 @@ class ExistingPackViewModel : ViewModel() {
is ExistingPackAction.AddNestedPack -> inputHandler.addNestedPack()
is ExistingPackAction.RemoveNestedPack -> inputHandler.removeNestedPack(existingPackAction.nestedPack)
is ExistingPackAction.PreviewPackObject -> previewIconPackObject()
is ExistingPackAction.SavePack -> saveIconPack()
is ExistingPackAction.SavePack -> saveIconPack(existingPackAction.project, existingPackAction.isModified)
}
}

Expand All @@ -85,6 +83,7 @@ class ExistingPackViewModel : ViewModel() {
_state.updateState {
ExistingPackEditState(
inputFieldState = inputFieldState,
initialInputFieldState = inputFieldState,
importDirectory = path.parent.absolutePathString(),
)
}
Expand All @@ -109,7 +108,7 @@ class ExistingPackViewModel : ViewModel() {
_events.send(ExistingPackEvent.PreviewIconPackObject(code = iconPackCode))
}

private fun saveIconPack() {
private fun saveIconPack(project: Project, isModified: Boolean) {
val editState = currentState.safeAs<ExistingPackEditState>() ?: return
val inputFieldState = editState.inputFieldState

Expand All @@ -118,12 +117,12 @@ class ExistingPackViewModel : ViewModel() {
iconPackDestination = editState.importDirectory
flatPackage = false
}
withContext(Dispatchers.IO) {
IconPackWriter.savePack(
inMemorySettings = inMemorySettings,
inputFieldState = inputFieldState,
)
}
IconPackWriter.savePack(
project = project,
inMemorySettings = inMemorySettings,
inputFieldState = inputFieldState,
writeToFile = isModified,
)
_events.send(ExistingPackEvent.OnSettingsUpdated)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ sealed interface ExistingPackAction {
data class RemoveNestedPack(val nestedPack: NestedPack) : ExistingPackAction

data object PreviewPackObject : ExistingPackAction
data object SavePack : ExistingPackAction
data class SavePack(val project: Project, val isModified: Boolean) : ExistingPackAction
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ sealed interface ExistingPackModeState {

data class ExistingPackEditState(
val inputFieldState: InputFieldState = InputFieldState.Empty,
val initialInputFieldState: InputFieldState = InputFieldState.Empty,
val importDirectory: String = "",
) : ExistingPackModeState
) : ExistingPackModeState {

val isModified: Boolean
get() = inputFieldState.license.text != initialInputFieldState.license.text ||
inputFieldState.nestedPacks.map { it.inputFieldState.text } !=
initialInputFieldState.nestedPacks.map { it.inputFieldState.text }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.github.composegears.valkyrie.jewel.platform.LocalProject
import io.github.composegears.valkyrie.jewel.tooling.ProjectPreviewTheme
import io.github.composegears.valkyrie.sdk.compose.foundation.layout.Spacer
import io.github.composegears.valkyrie.ui.screen.mode.iconpack.common.IconPackEditor
Expand All @@ -29,6 +30,8 @@ fun ExistingPackEditor(
onAction: (ExistingPackAction) -> Unit,
modifier: Modifier = Modifier,
) {
val project = LocalProject.current

Column(
modifier = modifier
.widthIn(max = 450.dp)
Expand All @@ -45,10 +48,21 @@ fun ExistingPackEditor(
Spacer(32.dp)
DefaultButton(
modifier = Modifier.align(Alignment.End),
onClick = { onAction(SavePack) },
onClick = {
onAction(
SavePack(
project = project,
isModified = state.isModified,
),
)
},
enabled = state.inputFieldState.isValid,
) {
Text(text = stringResource("iconpack.existingpack.editor.update.continue"))
if (state.isModified) {
Text(text = stringResource("iconpack.existingpack.editor.update.continue"))
} else {
Text(text = stringResource("iconpack.existingpack.editor.continue"))
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.composegears.valkyrie.ui.screen.mode.iconpack.material
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.composegears.leviathan.compose.inject
import com.intellij.openapi.project.Project
import io.github.composegears.valkyrie.parser.unified.util.PackageExtractor
import io.github.composegears.valkyrie.sdk.core.extensions.safeAs
import io.github.composegears.valkyrie.ui.di.DI
Expand All @@ -23,15 +24,13 @@ import io.github.composegears.valkyrie.ui.screen.mode.iconpack.material.model.Ma
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.invariantSeparatorsPathString
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class MaterialPackViewModel : ViewModel() {

Expand Down Expand Up @@ -81,7 +80,7 @@ class MaterialPackViewModel : ViewModel() {
saveDestination()
initMaterialPack()
}
is SavePack -> saveIconPack()
is SavePack -> saveIconPack(action.project)
}
}

Expand Down Expand Up @@ -114,20 +113,19 @@ class MaterialPackViewModel : ViewModel() {
}
}

private fun saveIconPack() {
private fun saveIconPack(project: Project) {
val pickedState = currentState.safeAs<PickedState>() ?: return

viewModelScope.launch {
inMemorySettings.update {
flatPackage = pickedState.flatPackageStructure
}
withContext(Dispatchers.IO) {
IconPackWriter.savePack(
writeToFile = false,
inMemorySettings = inMemorySettings,
inputFieldState = pickedState.inputFieldState,
)
}
IconPackWriter.savePack(
project = project,
writeToFile = false,
inMemorySettings = inMemorySettings,
inputFieldState = pickedState.inputFieldState,
)
_events.send(FinishSetup)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package io.github.composegears.valkyrie.ui.screen.mode.iconpack.material.model

import com.intellij.openapi.project.Project
import java.nio.file.Path

sealed interface MaterialPackAction {
data class SelectDestinationFolder(val path: Path) : MaterialPackAction
data class UpdateFlatPackageStructure(val value: Boolean) : MaterialPackAction
data object SaveDestination : MaterialPackAction

data object SavePack : MaterialPackAction
data class SavePack(val project: Project) : MaterialPackAction
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import io.github.composegears.valkyrie.jewel.platform.LocalProject
import io.github.composegears.valkyrie.jewel.settings.CheckboxSettingsRow
import io.github.composegears.valkyrie.jewel.settings.Group
import io.github.composegears.valkyrie.jewel.tooling.ProjectPreviewTheme
Expand All @@ -35,6 +36,8 @@ fun MaterialPackCreation(
onValueChange: (InputChange) -> Unit,
modifier: Modifier = Modifier,
) {
val project = LocalProject.current

Column(
modifier = modifier
.widthIn(max = 450.dp)
Expand Down Expand Up @@ -66,7 +69,7 @@ fun MaterialPackCreation(
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.End),
) {
DefaultButton(
onClick = { onAction(MaterialPackAction.SavePack) },
onClick = { onAction(MaterialPackAction.SavePack(project)) },
enabled = state.inputFieldState.isValid,
) {
Text(text = stringResource("iconpack.newpack.creation.continue"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.composegears.valkyrie.ui.screen.mode.iconpack.newpack
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.composegears.leviathan.compose.inject
import com.intellij.openapi.project.Project
import io.github.composegears.valkyrie.generator.core.IconPack
import io.github.composegears.valkyrie.generator.iconpack.IconPackGenerator
import io.github.composegears.valkyrie.generator.iconpack.IconPackGeneratorConfig
Expand All @@ -20,15 +21,13 @@ import io.github.composegears.valkyrie.ui.screen.mode.iconpack.newpack.model.New
import java.nio.file.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.invariantSeparatorsPathString
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class NewPackViewModel : ViewModel() {

Expand Down Expand Up @@ -75,7 +74,7 @@ class NewPackViewModel : ViewModel() {
is NewPackAction.AddNestedPack -> inputHandler.addNestedPack()
is NewPackAction.RemoveNestedPack -> inputHandler.removeNestedPack(action.nestedPack)
is NewPackAction.PreviewPackObject -> previewIconPackObject()
is NewPackAction.SavePack -> saveIconPack()
is NewPackAction.SavePack -> saveIconPack(action.project)
}
}

Expand Down Expand Up @@ -127,16 +126,15 @@ class NewPackViewModel : ViewModel() {
_events.send(NewPackEvent.PreviewIconPackObject(code = iconPackCode))
}

private fun saveIconPack() {
private fun saveIconPack(project: Project) {
val pickedState = currentState.safeAs<NewPackModeState.PickedState>() ?: return

viewModelScope.launch {
withContext(Dispatchers.IO) {
IconPackWriter.savePack(
inMemorySettings = inMemorySettings,
inputFieldState = pickedState.inputFieldState,
)
}
IconPackWriter.savePack(
project = project,
inMemorySettings = inMemorySettings,
inputFieldState = pickedState.inputFieldState,
)
_events.send(NewPackEvent.OnSettingsUpdated)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.composegears.valkyrie.ui.screen.mode.iconpack.newpack.model

import com.intellij.openapi.project.Project
import io.github.composegears.valkyrie.ui.screen.mode.iconpack.common.model.NestedPack
import java.nio.file.Path

Expand All @@ -11,5 +12,5 @@ sealed interface NewPackAction {
data class RemoveNestedPack(val nestedPack: NestedPack) : NewPackAction

data object PreviewPackObject : NewPackAction
data object SavePack : NewPackAction
data class SavePack(val project: Project) : NewPackAction
}
Loading
Loading