Skip to content

Commit 1d04e82

Browse files
committed
[IconPack] Generated ImageVector files are now appear immediately in the project tree without manual refresh
1 parent 4a2db2f commit 1d04e82

4 files changed

Lines changed: 91 additions & 30 deletions

File tree

tools/idea-plugin/CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313

1414
### Changed
1515

16-
- [IconPack] Don’t override the `.kt` file object if the content hasn’t changed
16+
- [IconPack] Don't override the `.kt` file object if the content hasn't changed
17+
- [IconPack] Generated ImageVector files are now appear immediately in the project tree without manual refresh
1718

1819
### Fixed
1920

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

2526
## 1.5.0 - 2026-04-17
2627

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.github.composegears.valkyrie.ui.screen.mode.iconpack.common.util
2+
3+
import com.intellij.openapi.project.Project
4+
import com.intellij.openapi.vfs.LocalFileSystem
5+
import com.intellij.platform.ide.progress.withBackgroundProgress
6+
import kotlin.io.path.Path
7+
import kotlin.io.path.createDirectories
8+
import kotlin.io.path.writeText
9+
import kotlinx.coroutines.Dispatchers
10+
import kotlinx.coroutines.ensureActive
11+
import kotlinx.coroutines.withContext
12+
13+
data class ImageVectorFile(
14+
val directoryPath: String,
15+
val fileName: String,
16+
val content: String,
17+
)
18+
19+
object ImageVectorWriter {
20+
21+
@Suppress("InconsistentCommentForJavaParameter")
22+
suspend fun saveVectors(
23+
project: Project,
24+
files: List<ImageVectorFile>,
25+
) {
26+
if (files.isEmpty()) return
27+
28+
withBackgroundProgress(
29+
project = project,
30+
title = "Generate ImageVector",
31+
) {
32+
val rootsToRefresh = withContext(Dispatchers.IO) {
33+
files
34+
.groupBy { Path(it.directoryPath) }
35+
.onEach { (dir, dirFiles) ->
36+
ensureActive()
37+
38+
dir.createDirectories()
39+
40+
dirFiles.forEach { (_, fileName, content) ->
41+
dir.resolve(fileName).writeText(content)
42+
}
43+
}
44+
.keys
45+
.toList()
46+
}
47+
48+
LocalFileSystem.getInstance()
49+
.refreshNioFiles(
50+
/* files = */
51+
rootsToRefresh,
52+
/* async = */
53+
true,
54+
/* recursive = */
55+
true,
56+
/* onFinish = */
57+
null,
58+
)
59+
}
60+
}
61+
}

tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionScreen.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ import com.composegears.tiamat.compose.navController
3232
import com.composegears.tiamat.compose.navDestination
3333
import com.composegears.tiamat.compose.navigate
3434
import com.composegears.tiamat.compose.saveableViewModel
35-
import com.intellij.openapi.vfs.VirtualFileManager
3635
import io.github.composegears.valkyrie.jewel.banner.BannerMessage.WarningBanner
3736
import io.github.composegears.valkyrie.jewel.banner.rememberBannerManager
37+
import io.github.composegears.valkyrie.jewel.platform.LocalProject
3838
import io.github.composegears.valkyrie.jewel.platform.rememberMultiSelectDragAndDropHandler
3939
import io.github.composegears.valkyrie.jewel.tooling.PreviewNavigationControls
4040
import io.github.composegears.valkyrie.jewel.tooling.PreviewTheme
@@ -68,6 +68,7 @@ import org.jetbrains.jewel.ui.component.Text
6868
val IconPackConversionScreen by navDestination<PendingPathData> {
6969
val navController = navController()
7070
val pendingData = navArgsOrNull()
71+
val project = LocalProject.current
7172

7273
val viewModel = saveableViewModel {
7374
IconPackConversionViewModel(
@@ -87,9 +88,6 @@ val IconPackConversionScreen by navDestination<PendingPathData> {
8788
navArgs = event.iconContent,
8889
)
8990
}
90-
is ConversionEvent.ImportCompleted -> {
91-
VirtualFileManager.getInstance().asyncRefresh()
92-
}
9391
is ConversionEvent.NothingToImport -> {
9492
bannerManager.show(message = WarningBanner(text = message("iconpack.conversion.nothing.import")))
9593
}
@@ -108,7 +106,7 @@ val IconPackConversionScreen by navDestination<PendingPathData> {
108106
onDeleteIcon = viewModel::deleteIcon,
109107
onReset = viewModel::reset,
110108
onPreviewClick = viewModel::showPreview,
111-
onImport = viewModel::import,
109+
onImport = { viewModel.import(project) },
112110
onRenameIcon = viewModel::renameIcon,
113111
onResolveIssues = viewModel::resolveImportIssues,
114112
)

tools/idea-plugin/src/main/kotlin/io/github/composegears/valkyrie/ui/screen/mode/iconpack/conversion/IconPackConversionViewModel.kt

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
55
import com.composegears.leviathan.compose.inject
66
import com.composegears.tiamat.navigation.MutableSavedState
77
import com.composegears.tiamat.navigation.recordOf
8+
import com.intellij.openapi.project.Project
89
import io.github.composegears.valkyrie.generator.jvm.imagevector.ImageVectorGenerator
910
import io.github.composegears.valkyrie.generator.jvm.imagevector.ImageVectorGeneratorConfig
1011
import io.github.composegears.valkyrie.parser.unified.ParserType
@@ -13,11 +14,12 @@ import io.github.composegears.valkyrie.parser.unified.ext.isSvg
1314
import io.github.composegears.valkyrie.parser.unified.ext.isXml
1415
import io.github.composegears.valkyrie.parser.unified.ext.toIOPath
1516
import io.github.composegears.valkyrie.sdk.core.extensions.safeAs
16-
import io.github.composegears.valkyrie.sdk.core.extensions.writeToKt
1717
import io.github.composegears.valkyrie.settings.ValkyriesSettings
1818
import io.github.composegears.valkyrie.ui.di.DI
1919
import io.github.composegears.valkyrie.ui.extension.updateState
2020
import io.github.composegears.valkyrie.ui.foundation.picker.PickerEvent
21+
import io.github.composegears.valkyrie.ui.screen.mode.iconpack.common.util.ImageVectorFile
22+
import io.github.composegears.valkyrie.ui.screen.mode.iconpack.common.util.ImageVectorWriter
2123
import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.ConversionEvent.OpenPreview
2224
import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.IconPackConversionState.BatchProcessing
2325
import io.github.composegears.valkyrie.ui.screen.mode.iconpack.conversion.IconPackConversionState.IconsPickering
@@ -36,7 +38,6 @@ import kotlinx.coroutines.flow.launchIn
3638
import kotlinx.coroutines.flow.onEach
3739
import kotlinx.coroutines.flow.receiveAsFlow
3840
import kotlinx.coroutines.launch
39-
import kotlinx.coroutines.withContext
4041

4142
class IconPackConversionViewModel(
4243
savedState: MutableSavedState,
@@ -171,7 +172,7 @@ class IconPackConversionViewModel(
171172
_events.send(OpenPreview(output.content))
172173
}
173174

174-
fun import() = viewModelScope.launch(Dispatchers.Default) {
175+
fun import(project: Project) = viewModelScope.launch(Dispatchers.Default) {
175176
val icons = when (val state = _state.value) {
176177
is BatchProcessing.IconPackCreationState -> state.icons
177178
else -> return@launch
@@ -181,9 +182,9 @@ class IconPackConversionViewModel(
181182

182183
val settings = inMemorySettings.current
183184

184-
icons
185+
val filesToWrite = icons
185186
.filterIsInstance<BatchIcon.Valid>()
186-
.forEach { icon ->
187+
.map { icon ->
187188
when (val iconPack = icon.iconPack) {
188189
is IconPack.Nested -> {
189190
val vectorSpecOutput = ImageVectorGenerator.convert(
@@ -194,16 +195,15 @@ class IconPackConversionViewModel(
194195
nestedPackName = iconPack.currentNestedPack,
195196
),
196197
)
197-
198-
withContext(Dispatchers.IO) {
199-
vectorSpecOutput.content.writeToKt(
200-
outputDir = when {
201-
settings.flatPackage -> settings.iconPackDestination
202-
else -> "${settings.iconPackDestination}/${iconPack.currentNestedPack.lowercase()}"
203-
},
204-
nameWithoutExtension = vectorSpecOutput.name,
205-
)
198+
val outputDir = when {
199+
settings.flatPackage -> settings.iconPackDestination
200+
else -> "${settings.iconPackDestination}/${iconPack.currentNestedPack.lowercase()}"
206201
}
202+
ImageVectorFile(
203+
directoryPath = outputDir,
204+
fileName = "${vectorSpecOutput.name}.kt",
205+
content = vectorSpecOutput.content,
206+
)
207207
}
208208
is IconPack.Single -> {
209209
val vectorSpecOutput = ImageVectorGenerator.convert(
@@ -214,18 +214,20 @@ class IconPackConversionViewModel(
214214
nestedPackName = "",
215215
),
216216
)
217-
218-
withContext(Dispatchers.IO) {
219-
vectorSpecOutput.content.writeToKt(
220-
outputDir = settings.iconPackDestination,
221-
nameWithoutExtension = vectorSpecOutput.name,
222-
)
223-
}
217+
ImageVectorFile(
218+
directoryPath = settings.iconPackDestination,
219+
fileName = "${vectorSpecOutput.name}.kt",
220+
content = vectorSpecOutput.content,
221+
)
224222
}
225223
}
226224
}
227225

228-
_events.send(ConversionEvent.ImportCompleted)
226+
ImageVectorWriter.saveVectors(
227+
project = project,
228+
files = filesToWrite,
229+
)
230+
229231
reset()
230232
}
231233

@@ -383,6 +385,5 @@ class IconPackConversionViewModel(
383385

384386
sealed interface ConversionEvent {
385387
data class OpenPreview(val iconContent: String) : ConversionEvent
386-
data object ImportCompleted : ConversionEvent
387388
data object NothingToImport : ConversionEvent
388389
}

0 commit comments

Comments
 (0)