From dcda8af7dab080b2e5c0f102c0c157f00c8eaaba Mon Sep 17 00:00:00 2001 From: Robert Jaros Date: Sun, 6 Dec 2020 12:46:07 +0100 Subject: [PATCH 1/8] Some minor GUI tweaks. Fixes for supported project types. Upgrade to KVision 3.17.2. --- README.md | 6 +- build.gradle | 6 +- .../stonks/kvizard/KVisionModuleBuilder.kt | 48 +++-- .../tech/stonks/kvizard/KVisionModuleType.kt | 18 +- .../stonks/kvizard/generator/TreeGenerator.kt | 98 ++++++---- .../step/library_choice/LibraryChoiceStep.kt | 4 +- .../step/library_choice/LibraryChoiceView.kt | 27 ++- .../stonks/kvizard/utils/KVisionDialogUtil.kt | 2 +- .../kvizard/utils/RunConfigurationUtil.kt | 16 +- .../kotlin/tech/stonks/kvizard/utils/file.kt | 4 +- src/main/resources/META-INF/plugin.xml | 33 ++-- .../fileTemplates/internal/Procfile.ft | 1 - .../fileTemplates/internal/app.json.ft | 4 - .../internal/front_build.gradle.kts.ft | 2 +- .../internal/front_gradle.properties.ft | 3 +- .../internal/front_settings.gradle.kts.ft | 2 +- .../frontend_source_frontend_App.kt.ft | 32 +++ ...ft => frontend_source_fullstack_App.kt.ft} | 0 ... => frontend_source_fullstack_Model.kt.ft} | 0 .../fileTemplates/internal/gradlew.bat.ft | 89 --------- .../fileTemplates/internal/gradlew.ft | 185 ------------------ .../internal/ktor_gradle.properties.ft | 2 + .../internal/runConfigurations.xml.ft | 38 ---- .../internal/spring_build.gradle.kts.ft | 12 +- .../internal/spring_gradle.properties.ft | 8 +- .../internal/spring_settings.gradle.kts.ft | 2 +- .../internal/webpack_frontend_webpack.js.ft | 6 + ....js.ft => webpack_fullstack_webpack.js.ft} | 4 - .../internal/webpack_minify.js.ft | 17 -- 29 files changed, 216 insertions(+), 453 deletions(-) delete mode 100644 src/main/resources/fileTemplates/internal/Procfile.ft delete mode 100644 src/main/resources/fileTemplates/internal/app.json.ft create mode 100644 src/main/resources/fileTemplates/internal/frontend_source_frontend_App.kt.ft rename src/main/resources/fileTemplates/internal/{frontend_source_App.kt.ft => frontend_source_fullstack_App.kt.ft} (100%) rename src/main/resources/fileTemplates/internal/{frontend_source_Model.kt.ft => frontend_source_fullstack_Model.kt.ft} (100%) delete mode 100644 src/main/resources/fileTemplates/internal/gradlew.bat.ft delete mode 100644 src/main/resources/fileTemplates/internal/gradlew.ft delete mode 100644 src/main/resources/fileTemplates/internal/runConfigurations.xml.ft create mode 100644 src/main/resources/fileTemplates/internal/webpack_frontend_webpack.js.ft rename src/main/resources/fileTemplates/internal/{webpack_webpack.js.ft => webpack_fullstack_webpack.js.ft} (61%) delete mode 100644 src/main/resources/fileTemplates/internal/webpack_minify.js.ft diff --git a/README.md b/README.md index 1aba670..1dcf457 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ # kvision-project-wizard KVision plugin for project creation. Supported project types: - * Fullstack KTOR - * Fullstack Spring-Boot - * Frontend-only + * Frontend template + * Ktor fullstack project + * Spring Boot fullstack project ## Contribution If you want to contribute, You can add support for other backend frameworks like javalin, jooby, vertx and others. To do so You must: diff --git a/build.gradle b/build.gradle index 64d42e4..c4e1100 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { - id 'org.jetbrains.intellij' version '0.5.0' - id 'org.jetbrains.kotlin.jvm' version '1.4.10' + id 'org.jetbrains.intellij' version '0.6.5' + id 'org.jetbrains.kotlin.jvm' version '1.4.20' id 'idea' } @@ -23,7 +23,7 @@ dependencies { // See https://github.com/JetBrains/gradle-intellij-plugin/ intellij { - version '2020.2.1' + version '2020.3' plugins = ['gradle', 'java', 'org.jetbrains.kotlin'] } patchPluginXml { diff --git a/src/main/kotlin/tech/stonks/kvizard/KVisionModuleBuilder.kt b/src/main/kotlin/tech/stonks/kvizard/KVisionModuleBuilder.kt index e739819..71bab78 100644 --- a/src/main/kotlin/tech/stonks/kvizard/KVisionModuleBuilder.kt +++ b/src/main/kotlin/tech/stonks/kvizard/KVisionModuleBuilder.kt @@ -1,6 +1,5 @@ package tech.stonks.kvizard -import com.intellij.ide.fileTemplates.FileTemplateManager import com.intellij.ide.util.projectWizard.ModuleBuilder import com.intellij.ide.util.projectWizard.ModuleWizardStep import com.intellij.ide.util.projectWizard.WizardContext @@ -19,7 +18,10 @@ import tech.stonks.kvizard.generator.KtorTreeGenerator import tech.stonks.kvizard.generator.SpringTreeGenerator import tech.stonks.kvizard.generator.TreeGenerator import tech.stonks.kvizard.step.library_choice.LibraryChoiceStep -import tech.stonks.kvizard.utils.* +import tech.stonks.kvizard.utils.KVisionDialogUtil +import tech.stonks.kvizard.utils.RunConfigurationUtil +import tech.stonks.kvizard.utils.backgroundTask +import tech.stonks.kvizard.utils.runGradle import java.io.File class KVisionModuleBuilder : ModuleBuilder() { @@ -28,14 +30,14 @@ class KVisionModuleBuilder : ModuleBuilder() { /** * Here add libraries that were newly supported */ - val supportedBackendLibraries = arrayOf( - KVisionBackendLibrary.KTOR, - KVisionBackendLibrary.SPRING_BOOT, - KVisionBackendLibrary.FRONTEND_ONLY + val supportedProjectTypes = arrayOf( + KVisionProjectType.FRONTEND_ONLY, + KVisionProjectType.KTOR, + KVisionProjectType.SPRING_BOOT ) } - var backendLibrary: KVisionBackendLibrary = KVisionBackendLibrary.KTOR + var projectType: KVisionProjectType = KVisionProjectType.FRONTEND_ONLY var groupId: String = "com.example" var artifactId: String = "project" @@ -58,24 +60,32 @@ class KVisionModuleBuilder : ModuleBuilder() { try { generator.generate(root, artifactId, groupId) } catch (ex: Exception) { - } - runGradleTasks(modifiableRootModel.project) - RunConfigurationUtil.createConfiguration(modifiableRootModel.project) + installGradleWrapper(modifiableRootModel.project) + if (projectType == KVisionProjectType.FRONTEND_ONLY) { + RunConfigurationUtil.createFrontendConfiguration(modifiableRootModel.project) + } else { + runCompileMetadata(modifiableRootModel.project) + RunConfigurationUtil.createFullstackConfiguration(modifiableRootModel.project) + } KVisionDialogUtil.showNewsDialog() } } - private fun runGradleTasks(project: Project) { + private fun runCompileMetadata(project: Project) { project.runGradle("compileKotlinMetadata") } + private fun installGradleWrapper(project: Project) { + project.runGradle("wrapper --gradle-version 6.7.1 --distribution-type all") + } + private fun createGenerator(): TreeGenerator { - return when (backendLibrary) { - KVisionBackendLibrary.KTOR -> KtorTreeGenerator() - KVisionBackendLibrary.FRONTEND_ONLY -> FrontendTreeGenerator() - KVisionBackendLibrary.SPRING_BOOT -> SpringTreeGenerator() - else -> throw IllegalStateException("${backendLibrary.name} is not supported yet.") + return when (projectType) { + KVisionProjectType.KTOR -> KtorTreeGenerator() + KVisionProjectType.FRONTEND_ONLY -> FrontendTreeGenerator() + KVisionProjectType.SPRING_BOOT -> SpringTreeGenerator() + else -> throw IllegalStateException("${projectType.name} is not supported yet.") } } @@ -88,9 +98,7 @@ class KVisionModuleBuilder : ModuleBuilder() { return KVisionModuleType() } - override fun getCustomOptionsStep(context: WizardContext?, parentDisposable: Disposable?): ModuleWizardStep? { + override fun getCustomOptionsStep(context: WizardContext?, parentDisposable: Disposable?): ModuleWizardStep { return LibraryChoiceStep(this) } - - -} \ No newline at end of file +} diff --git a/src/main/kotlin/tech/stonks/kvizard/KVisionModuleType.kt b/src/main/kotlin/tech/stonks/kvizard/KVisionModuleType.kt index af0affc..87373c5 100644 --- a/src/main/kotlin/tech/stonks/kvizard/KVisionModuleType.kt +++ b/src/main/kotlin/tech/stonks/kvizard/KVisionModuleType.kt @@ -6,18 +6,18 @@ import javax.swing.Icon class KVisionModuleType : ModuleType("KVISION_WIZARD") { - private val _icon: Icon by lazy { IconLoader.getIcon("/images/logo16.png") } + private val _icon: Icon by lazy { IconLoader.getIcon("/images/logo16.png", KVisionModuleType::class.java) } override fun createModuleBuilder(): KVisionModuleBuilder { return KVisionModuleBuilder() } override fun getName(): String { - return "KVision Project" + return "KVision" } override fun getDescription(): String { - return "KVision Project wizard. It is a fullstack kotlin framework" + return "A new project with KVision - an object oriented web framework for Kotlin/JS" } override fun getNodeIcon(isOpened: Boolean): Icon { @@ -29,6 +29,12 @@ class KVisionModuleType : ModuleType("KVISION_WIZARD") { } } -enum class KVisionBackendLibrary { - KTOR, FRONTEND_ONLY, JAVALIN, JOOBY, MICRONAUT, SPRING_BOOT, VERTX; -} \ No newline at end of file +enum class KVisionProjectType(val displayName: String) { + FRONTEND_ONLY("Frontend template"), + KTOR("Ktor fullstack project"), + SPRING_BOOT("Spring Boot fullstack project"), + JAVALIN("Javalin fullstack project"), + JOOBY("Jooby fullstack project"), + MICRONAUT("Micronaut fullstack project"), + VERTX("Vert.x fullstack project"); +} diff --git a/src/main/kotlin/tech/stonks/kvizard/generator/TreeGenerator.kt b/src/main/kotlin/tech/stonks/kvizard/generator/TreeGenerator.kt index 22fd608..e938ae1 100644 --- a/src/main/kotlin/tech/stonks/kvizard/generator/TreeGenerator.kt +++ b/src/main/kotlin/tech/stonks/kvizard/generator/TreeGenerator.kt @@ -2,8 +2,17 @@ package tech.stonks.kvizard.generator import com.intellij.openapi.vfs.VirtualFile import tech.stonks.kvizard.data.VersionApi -import tech.stonks.kvizard.data.model.* -import tech.stonks.kvizard.utils.* +import tech.stonks.kvizard.data.model.TemplateJooby +import tech.stonks.kvizard.data.model.TemplateKtor +import tech.stonks.kvizard.data.model.TemplateMicronaut +import tech.stonks.kvizard.data.model.TemplateSpring +import tech.stonks.kvizard.data.model.TemplateVertx +import tech.stonks.kvizard.data.model.VersionData +import tech.stonks.kvizard.utils.TemplateAttributes +import tech.stonks.kvizard.utils.build +import tech.stonks.kvizard.utils.dir +import tech.stonks.kvizard.utils.file +import tech.stonks.kvizard.utils.packages /** * Base class for building KVision project. @@ -35,11 +44,7 @@ abstract class TreeGenerator( private val rootFiles: Array = arrayOf( ".gettext.json", ".gitignore", - "app.json", - "system.properties", - "gradlew.bat", - "gradlew", - "Procfile" + "system.properties" ), private val webpackFiles: Array = arrayOf( "bootstrap.js", @@ -47,14 +52,18 @@ abstract class TreeGenerator( "file.js", "handlebars.js", "jquery.js", - "minify.js", - "moment.js", + "moment.js" + ), + private val webpackCustomFiles: Array = arrayOf( "webpack.js" ), private val commonFiles: Array = arrayOf( "Service.kt" ), - private val frontendSourceFiles: Array = arrayOf( + private val frontendSourceFrontendFiles: Array = arrayOf( + "App.kt" + ), + private val frontendSourceFullstackFiles: Array = arrayOf( "App.kt", "Model.kt" ), @@ -72,10 +81,10 @@ abstract class TreeGenerator( fun generate(root: VirtualFile, artifactId: String, groupId: String) { try { val packageSegments = groupId - .split(".") - .toMutableList() - .apply { add(artifactId) } - .toList() + .split(".") + .toMutableList() + .apply { add(artifactId) } + .toList() val attrs = generateAttributes(artifactId, groupId) root.build { dir("src") { @@ -102,28 +111,40 @@ abstract class TreeGenerator( } } } - } - dir("commonMain") { - dir("kotlin") { - packages(packageSegments) { - commonFiles.forEach { fileName -> file(fileName, "common_$fileName", attrs) } + dir("commonMain") { + dir("kotlin") { + packages(packageSegments) { + commonFiles.forEach { fileName -> file(fileName, "common_$fileName", attrs) } + } } } } - dir("frontendMain") { + val frontendMain = if (isFrontendOnly) "main" else "frontendMain" + dir(frontendMain) { dir("kotlin") { packages(packageSegments) { - frontendSourceFiles.forEach { fileName -> - file( - fileName, - "frontend_source_$fileName", - attrs - ) + if (isFrontendOnly) { + frontendSourceFrontendFiles.forEach { fileName -> + file( + fileName, + "frontend_source_frontend_$fileName", + attrs + ) + } + } else { + frontendSourceFullstackFiles.forEach { fileName -> + file( + fileName, + "frontend_source_fullstack_$fileName", + attrs + ) + } } } } dir("web") { frontendWebFiles.forEach { fileName -> file(fileName, "frontend_web_$fileName", attrs) } + } dir("resources") { dir("i18n") { @@ -137,7 +158,8 @@ abstract class TreeGenerator( } } } - dir("frontendTest") { + val frontendTest = if (isFrontendOnly) "test" else "frontendTest" + dir(frontendTest) { dir("kotlin") { dir("test") { packages(packageSegments) { @@ -158,11 +180,15 @@ abstract class TreeGenerator( dir("wrapper") { } } - dir("idea.") { + dir(".idea") { ideaFiles.forEach { fileName -> file(fileName, "idea_${fileName}", attrs) } } dir("webpack.config.d") { webpackFiles.forEach { fileName -> file(fileName, "webpack_${fileName}", attrs) } + val customPrefix = if (isFrontendOnly) "frontend" else "fullstack" + webpackCustomFiles.forEach { fileName -> + file(fileName, "webpack_${customPrefix}_${fileName}", attrs) + } } gradleFile.forEach { fileName -> file(fileName, "${templateName}_${fileName}", attrs) } rootFiles.forEach { fileName -> file(fileName, fileName, attrs) } @@ -200,16 +226,16 @@ abstract class TreeGenerator( VersionApi.create().getVersionData().blockingGet() } catch (ex: Exception) { VersionData( - kVision = "3.16.3", - kotlin = "1.4.10", + kVision = "3.17.2", + kotlin = "1.4.20", serialization = "1.0.1", - coroutines = "1.3.9", - templateJooby = TemplateJooby("2.9.2"), - templateKtor = TemplateKtor("1.4.2"), - templateMicronaut = TemplateMicronaut("2.1.3"), + coroutines = "1.4.2", + templateJooby = TemplateJooby("2.9.4"), + templateKtor = TemplateKtor("1.4.1"), + templateMicronaut = TemplateMicronaut("2.2.0"), templateSpring = TemplateSpring( - springBoot = "2.3.5.RELEASE", - springDataR2dbc = "1.1.5.RELEASE", + springBoot = "2.4.0", + springDataR2dbc = "1.2.1", r2dbcPostgres = "0.8.6.RELEASE", r2dbcH2 = "0.8.4.RELEASE" ), diff --git a/src/main/kotlin/tech/stonks/kvizard/step/library_choice/LibraryChoiceStep.kt b/src/main/kotlin/tech/stonks/kvizard/step/library_choice/LibraryChoiceStep.kt index 1f9288f..e31a69c 100644 --- a/src/main/kotlin/tech/stonks/kvizard/step/library_choice/LibraryChoiceStep.kt +++ b/src/main/kotlin/tech/stonks/kvizard/step/library_choice/LibraryChoiceStep.kt @@ -7,7 +7,7 @@ import javax.swing.JComponent class LibraryChoiceStep(private val _builder: KVisionModuleBuilder): ModuleWizardStep() { private val _view: LibraryChoiceView by lazy { LibraryChoiceView( - _builder.backendLibrary, _builder.groupId, _builder.artifactId + _builder.projectType, _builder.groupId, _builder.artifactId ).apply { onChanged = { updateDataModel() @@ -19,7 +19,7 @@ class LibraryChoiceStep(private val _builder: KVisionModuleBuilder): ModuleWizar } override fun updateDataModel() { - _builder.backendLibrary = _view.backendLibrary + _builder.projectType = _view.projectType _builder.groupId = _view.groupId _builder.artifactId = _view.artifactId } diff --git a/src/main/kotlin/tech/stonks/kvizard/step/library_choice/LibraryChoiceView.kt b/src/main/kotlin/tech/stonks/kvizard/step/library_choice/LibraryChoiceView.kt index bbf9ff4..16a7076 100644 --- a/src/main/kotlin/tech/stonks/kvizard/step/library_choice/LibraryChoiceView.kt +++ b/src/main/kotlin/tech/stonks/kvizard/step/library_choice/LibraryChoiceView.kt @@ -2,15 +2,23 @@ package tech.stonks.kvizard.step.library_choice import com.intellij.ide.BrowserUtil import com.intellij.openapi.ui.ComboBox -import tech.stonks.kvizard.KVisionBackendLibrary +import tech.stonks.kvizard.KVisionProjectType import tech.stonks.kvizard.KVisionModuleBuilder import tech.stonks.kvizard.utils.setOnTextChangedListener import java.awt.Color import java.awt.FlowLayout -import javax.swing.* +import javax.swing.BoxLayout +import javax.swing.JButton +import javax.swing.JComponent +import javax.swing.JLabel +import javax.swing.JPanel +import javax.swing.JTextField +import java.awt.Dimension +import javax.swing.Box + class LibraryChoiceView( - var backendLibrary: KVisionBackendLibrary, + var projectType: KVisionProjectType, var groupId: String, var artifactId: String ) : JPanel() { @@ -23,18 +31,18 @@ class LibraryChoiceView( val panel = JPanel().apply { layout = BoxLayout(this, BoxLayout.Y_AXIS) alignmentX = JComponent.LEFT_ALIGNMENT - add(JLabel("Choose your backend library").apply { alignmentX = LEFT_ALIGNMENT }) - add(ComboBox(KVisionModuleBuilder.supportedBackendLibraries).apply { + add(JLabel("Project type:").apply { alignmentX = LEFT_ALIGNMENT }) + add(ComboBox(KVisionModuleBuilder.supportedProjectTypes.map { it.displayName }.toTypedArray()).apply { alignmentX = LEFT_ALIGNMENT - prototypeDisplayValue = backendLibrary + setMinimumAndPreferredWidth(250) addItemListener { event: java.awt.event.ItemEvent -> if (event.stateChange == java.awt.event.ItemEvent.SELECTED) { - backendLibrary = event.item as KVisionBackendLibrary + projectType = KVisionProjectType.values().find { it.displayName == event.item }!! onChanged() } } }) - add(JLabel("GroupId").apply { alignmentX = LEFT_ALIGNMENT }) + add(JLabel("GroupId:").apply { alignmentX = LEFT_ALIGNMENT }) add(JTextField(groupId).apply { alignmentX = LEFT_ALIGNMENT setOnTextChangedListener { @@ -42,7 +50,7 @@ class LibraryChoiceView( onChanged() } }) - add(JLabel("ArtifactId").apply { alignmentX = LEFT_ALIGNMENT }) + add(JLabel("ArtifactId:").apply { alignmentX = LEFT_ALIGNMENT }) add(JTextField(artifactId).apply { alignmentX = LEFT_ALIGNMENT setOnTextChangedListener { @@ -50,6 +58,7 @@ class LibraryChoiceView( onChanged() } }) + add(Box.createRigidArea(Dimension(0, 20))) add(JButton("Check Kotlin.News").apply { background = Color(0xffe017) diff --git a/src/main/kotlin/tech/stonks/kvizard/utils/KVisionDialogUtil.kt b/src/main/kotlin/tech/stonks/kvizard/utils/KVisionDialogUtil.kt index 56f8085..dc3ae06 100644 --- a/src/main/kotlin/tech/stonks/kvizard/utils/KVisionDialogUtil.kt +++ b/src/main/kotlin/tech/stonks/kvizard/utils/KVisionDialogUtil.kt @@ -20,7 +20,7 @@ object KVisionDialogUtil { "You have got invitation to join our community! Join Kotlin.News and become ninja developer in Kotlin.", NotificationType.INFORMATION ).apply { - icon = IconLoader.getIcon("/images/email-black-18dp.svg") + icon = IconLoader.getIcon("/images/email-black-18dp.svg", KVisionDialogUtil::class.java) this.addAction(NewsAction("Join Kotlin.News")) this.addAction(NotShowAction("Do not show again")) } diff --git a/src/main/kotlin/tech/stonks/kvizard/utils/RunConfigurationUtil.kt b/src/main/kotlin/tech/stonks/kvizard/utils/RunConfigurationUtil.kt index 820b0de..408afbb 100644 --- a/src/main/kotlin/tech/stonks/kvizard/utils/RunConfigurationUtil.kt +++ b/src/main/kotlin/tech/stonks/kvizard/utils/RunConfigurationUtil.kt @@ -27,7 +27,7 @@ class KVisionConfigurationFactory(val task: String, private val args: String = " override fun getName(): String = "Run $task" - override fun getIcon(): Icon = IconLoader.getIcon("/images/logo16.png") + override fun getIcon(): Icon = IconLoader.getIcon("/images/logo16.png", KVisionConfigurationFactory::class.java) } class RunnerComparator : Comparator { @@ -41,7 +41,19 @@ class RunnerComparator : Comparator { } object RunConfigurationUtil { - fun createConfiguration(project: Project) { + fun createFrontendConfiguration(project: Project) { + val runManager = RunManagerImpl.getInstanceImpl(project) + runManager.addConfiguration( + RunnerAndConfigurationSettingsImpl( + RunManagerImpl.getInstanceImpl(project), + KVisionConfigurationFactory("run", "-t").createTemplateConfiguration(project) + ) + ) + runManager.setOrder(RunnerComparator()) + runManager.requestSort() + } + + fun createFullstackConfiguration(project: Project) { val runManager = RunManagerImpl.getInstanceImpl(project) runManager.addConfiguration( RunnerAndConfigurationSettingsImpl( diff --git a/src/main/kotlin/tech/stonks/kvizard/utils/file.kt b/src/main/kotlin/tech/stonks/kvizard/utils/file.kt index bdcb761..60294bc 100644 --- a/src/main/kotlin/tech/stonks/kvizard/utils/file.kt +++ b/src/main/kotlin/tech/stonks/kvizard/utils/file.kt @@ -61,8 +61,8 @@ fun String.insertAfter(after: Regex, insert: String): String { return if(last != null) { buildString { append(this.substring(0, last+1)) - appendln(insert) - appendln(this.substring(last+1)) + appendLine(insert) + appendLine(this.substring(last+1)) } }else { this diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index f4e2cee..1937c11 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -3,19 +3,30 @@ KVision Project Wizard Stonks.tech - version 0.2.2: - * Add more detailed description - version 0.2.1: - * Add icon and description - version 0.2.0: - * Added spring support - * Added run configurations for backendRun and frontendRun - version 0.1.0: - * added support for ktor + <i>Version 0.2.2:</i> + <ul> + <li>Add more detailed description</li> + </ul> + + <i>Version 0.2.1:</i> + <ul> + <li>Add icon and description</li> + </ul> + + <i>Version 0.2.0:</i> + <ul> + <li>Added spring support</li> + <li>Added run configurations for backendRun and frontendRun</li> + </ul> + + <i>Version 0.1.0:</i> + <ul> + <li>Added support for Ktor</li> + </ul> - Simple project wizard for KVision based projects. It allows You to create KVision projects based on Spring and Ktor (more supported backend frameworks in future). - To use it, click File -> New -> Project. In left panel choose KVision and then choose Your preferred backend framework or just simple frontend project. To run project choose "Frontend Run" and/or "Backend Run". + Simple project wizard for KVision based projects. It allows you to create KVision frontend and fullstack projects. + To use it, click File -> New -> Project. In left panel choose KVision and then choose your preferred project type.