Skip to content

Commit c3186ed

Browse files
timilirisclaude
andcommitted
v1.3.1 - Assets Explorer & Deploy Fixes
Added: - Assets Explorer tab with tree view (By Type / By Folder modes) - ZIP archive support for Assets.zip in server/ directory - Preview panels for images, JSON, YAML, and audio (OGG support) - Search, filter, and context menu actions Fixed: - Build & Deploy now correctly deploys to server/mods/ instead of server/plugins/ (#2) - Gradle deployToServer task dependency issue (#3) - Maven Build & Deploy: added auto-copy to server/mods during package phase Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 029c455 commit c3186ed

File tree

16 files changed

+2741
-12
lines changed

16 files changed

+2741
-12
lines changed

CHANGELOG.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,25 @@
22

33
## [Unreleased]
44

5+
## [1.3.1] - 2026-01-18
6+
7+
### Added
8+
9+
- **Assets Explorer**: New "Assets" tab in tool window for browsing game assets
10+
- Tree view with toggle between "By Type" and "By Folder" modes
11+
- Support for ZIP archives (Assets.zip in server/ directory)
12+
- Preview panels for images, JSON, YAML, and audio files (including OGG)
13+
- Search and filter by asset type
14+
- Context menu: open in editor, reveal in project, copy path, extract from ZIP
15+
- Drag & drop to import assets
16+
- Statistics bar showing asset counts and sizes
17+
18+
### Fixed
19+
20+
- **Build & Deploy configuration**: Now correctly deploys to `server/mods/` instead of `server/plugins/` ([#2](https://github.com/timiliris/hytaledDocs-intelliJ-plugin/issues/2))
21+
- **deployToServer Gradle task**: Fixed task dependency issue causing build failures ([#3](https://github.com/timiliris/hytaledDocs-intelliJ-plugin/issues/3))
22+
- **Maven Build & Deploy**: Added maven-resources-plugin to auto-copy JAR to server/mods during package phase
23+
524
## [1.3.0] - 2026-01-18
625

726
### Added
@@ -76,7 +95,8 @@
7695
- Supports IntelliJ IDEA 2024.3+
7796
- Requires Java 25 for Hytale development
7897

79-
[Unreleased]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.0...HEAD
98+
[Unreleased]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.1...HEAD
99+
[1.3.1]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.3.0...v1.3.1
80100
[1.3.0]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.2.0...v1.3.0
81101
[1.2.0]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.1.0...v1.2.0
82102
[1.1.0]: https://github.com/HytaleDocs/hytale-intellij-plugin/compare/v1.0.0...v1.1.0

build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ kotlin {
2222
// Configure project's dependencies
2323
repositories {
2424
mavenCentral()
25+
// JitPack for java-vorbis-support library (OGG audio playback)
26+
maven { url = uri("https://jitpack.io") }
2527

2628
// IntelliJ Platform Gradle Plugin Repositories Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-repositories-extension.html
2729
intellijPlatform {
@@ -35,6 +37,8 @@ dependencies {
3537
implementation("org.apache.commons:commons-compress:1.26.0")
3638
// Gson for JSON parsing
3739
implementation("com.google.code.gson:gson:2.11.0")
40+
// OGG Vorbis audio support for asset preview
41+
implementation("com.github.trilarion:java-vorbis-support:1.2.1")
3842

3943
testImplementation(libs.junit)
4044
testImplementation(libs.opentest4j)

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pluginGroup = com.hytaledocs.hytale
44
pluginName = Hytale Development Tools
55
pluginRepositoryUrl = https://github.com/HytaleDocs/hytale-intellij-plugin
66
# SemVer format -> https://semver.org
7-
pluginVersion = 1.3.0
7+
pluginVersion = 1.3.1
88

99
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
1010
pluginSinceBuild = 253
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package com.hytaledocs.intellij.assets
2+
3+
import com.intellij.openapi.vfs.VirtualFile
4+
import java.io.File
5+
6+
/**
7+
* Sealed class hierarchy representing nodes in the asset tree.
8+
* Supports two view modes: by type (category) and by folder structure.
9+
*/
10+
sealed class AssetNode {
11+
abstract val displayName: String
12+
abstract val sortKey: String
13+
14+
/**
15+
* Root node containing all assets.
16+
*/
17+
data class RootNode(
18+
override val displayName: String = "Assets",
19+
val children: MutableList<AssetNode> = mutableListOf()
20+
) : AssetNode() {
21+
override val sortKey: String = ""
22+
}
23+
24+
/**
25+
* Category node grouping assets by type (e.g., Textures, Models, Sounds).
26+
* Used in "By Type" view mode.
27+
*/
28+
data class CategoryNode(
29+
val assetType: AssetType,
30+
override val displayName: String = assetType.displayName,
31+
val children: MutableList<AssetNode> = mutableListOf(),
32+
var fileCount: Int = 0,
33+
var totalSize: Long = 0
34+
) : AssetNode() {
35+
override val sortKey: String = displayName.lowercase()
36+
}
37+
38+
/**
39+
* Folder node representing a directory in the file system.
40+
* Used in "By Folder" view mode.
41+
*/
42+
data class FolderNode(
43+
override val displayName: String,
44+
val path: String,
45+
val virtualFile: VirtualFile? = null,
46+
val children: MutableList<AssetNode> = mutableListOf(),
47+
var fileCount: Int = 0,
48+
var totalSize: Long = 0
49+
) : AssetNode() {
50+
override val sortKey: String = "0_$displayName".lowercase() // Sort folders before files
51+
}
52+
53+
/**
54+
* File node representing an individual asset file.
55+
*/
56+
data class FileNode(
57+
override val displayName: String,
58+
val file: File?,
59+
val virtualFile: VirtualFile? = null,
60+
val assetType: AssetType,
61+
val size: Long,
62+
val relativePath: String,
63+
val zipSource: ZipSource? = null
64+
) : AssetNode() {
65+
override val sortKey: String = "1_$displayName".lowercase() // Sort files after folders
66+
67+
val extension: String
68+
get() = displayName.substringAfterLast('.', "").lowercase()
69+
70+
val sizeFormatted: String
71+
get() = formatFileSize(size)
72+
73+
val isInZip: Boolean
74+
get() = zipSource != null
75+
76+
companion object {
77+
fun formatFileSize(bytes: Long): String {
78+
return when {
79+
bytes < 1024 -> "$bytes B"
80+
bytes < 1024 * 1024 -> "${bytes / 1024} KB"
81+
bytes < 1024 * 1024 * 1024 -> String.format("%.1f MB", bytes / (1024.0 * 1024))
82+
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
83+
}
84+
}
85+
}
86+
}
87+
88+
/**
89+
* ZIP archive node representing a ZIP file containing assets.
90+
*/
91+
data class ZipNode(
92+
override val displayName: String,
93+
val zipFile: File,
94+
val virtualFile: VirtualFile? = null,
95+
val children: MutableList<AssetNode> = mutableListOf(),
96+
var fileCount: Int = 0,
97+
var totalSize: Long = 0
98+
) : AssetNode() {
99+
override val sortKey: String = "0_$displayName".lowercase() // Sort ZIP files like folders
100+
}
101+
}
102+
103+
/**
104+
* Information about a file's source ZIP archive.
105+
*/
106+
data class ZipSource(
107+
val zipFile: File,
108+
val entryPath: String
109+
)
110+
111+
/**
112+
* Enum representing the view mode for the asset tree.
113+
*/
114+
enum class AssetViewMode(val displayName: String, val displayNameFr: String) {
115+
BY_TYPE("By Type", "Par type"),
116+
BY_FOLDER("By Folder", "Par dossier");
117+
118+
fun toggle(): AssetViewMode = when (this) {
119+
BY_TYPE -> BY_FOLDER
120+
BY_FOLDER -> BY_TYPE
121+
}
122+
}
123+
124+
/**
125+
* Data class for asset statistics.
126+
*/
127+
data class AssetStats(
128+
val totalFiles: Int = 0,
129+
val totalSize: Long = 0,
130+
val byType: Map<AssetType, Int> = emptyMap()
131+
) {
132+
val totalSizeFormatted: String
133+
get() = AssetNode.FileNode.formatFileSize(totalSize)
134+
135+
companion object {
136+
val EMPTY = AssetStats()
137+
}
138+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
package com.hytaledocs.intellij.assets
2+
3+
import com.hytaledocs.intellij.ui.HytaleTheme
4+
import com.intellij.icons.AllIcons
5+
import com.intellij.util.ui.JBUI
6+
import com.intellij.util.ui.UIUtil
7+
import java.awt.Component
8+
import java.awt.Font
9+
import javax.swing.JLabel
10+
import javax.swing.JTree
11+
import javax.swing.tree.DefaultMutableTreeNode
12+
import javax.swing.tree.DefaultTreeCellRenderer
13+
14+
/**
15+
* Custom tree cell renderer for the asset tree.
16+
* Displays icons, names, and metadata for each node type.
17+
*/
18+
class AssetTreeCellRenderer : DefaultTreeCellRenderer() {
19+
20+
private val folderIcon = AllIcons.Nodes.Folder
21+
private val folderOpenIcon = AllIcons.Actions.ProjectDirectory
22+
private val zipIcon = AllIcons.FileTypes.Archive
23+
24+
init {
25+
// Make renderer transparent to fix color mismatch
26+
isOpaque = false
27+
backgroundNonSelectionColor = null
28+
backgroundSelectionColor = UIUtil.getTreeSelectionBackground(true)
29+
borderSelectionColor = null
30+
31+
// Improve spacing
32+
iconTextGap = JBUI.scale(6)
33+
}
34+
35+
override fun getTreeCellRendererComponent(
36+
tree: JTree?,
37+
value: Any?,
38+
sel: Boolean,
39+
expanded: Boolean,
40+
leaf: Boolean,
41+
row: Int,
42+
hasFocus: Boolean
43+
): Component {
44+
val node = value as? DefaultMutableTreeNode
45+
val data = node?.userObject
46+
47+
when (data) {
48+
is AssetNode.RootNode -> {
49+
text = data.displayName
50+
icon = folderOpenIcon
51+
toolTipText = null
52+
font = tree?.font?.deriveFont(Font.BOLD)
53+
}
54+
55+
is AssetNode.CategoryNode -> {
56+
val countText = "${data.fileCount} files"
57+
val sizeText = AssetNode.FileNode.formatFileSize(data.totalSize)
58+
text = "${data.displayName} ($countText, $sizeText)"
59+
icon = data.assetType.icon
60+
toolTipText = "${data.displayName}: $countText, $sizeText"
61+
font = tree?.font?.deriveFont(Font.BOLD)
62+
}
63+
64+
is AssetNode.FolderNode -> {
65+
val countText = "${data.fileCount} files"
66+
text = data.displayName
67+
icon = if (expanded) folderOpenIcon else folderIcon
68+
toolTipText = "${data.path}: $countText"
69+
font = tree?.font?.deriveFont(Font.PLAIN)
70+
}
71+
72+
is AssetNode.ZipNode -> {
73+
val countText = "${data.fileCount} files"
74+
val sizeText = AssetNode.FileNode.formatFileSize(data.totalSize)
75+
text = "${data.displayName} ($countText, $sizeText)"
76+
icon = zipIcon
77+
toolTipText = "${data.displayName}: $countText, $sizeText"
78+
font = tree?.font?.deriveFont(Font.BOLD)
79+
}
80+
81+
is AssetNode.FileNode -> {
82+
text = data.displayName
83+
icon = data.assetType.icon
84+
toolTipText = "${data.relativePath} (${data.sizeFormatted})"
85+
font = tree?.font?.deriveFont(Font.PLAIN)
86+
}
87+
88+
else -> {
89+
text = value?.toString() ?: ""
90+
icon = folderIcon
91+
toolTipText = null
92+
font = tree?.font?.deriveFont(Font.PLAIN)
93+
}
94+
}
95+
96+
// Set colors based on selection
97+
if (sel) {
98+
foreground = UIUtil.getTreeSelectionForeground(true)
99+
background = UIUtil.getTreeSelectionBackground(true)
100+
isOpaque = true
101+
} else {
102+
foreground = when (data) {
103+
is AssetNode.CategoryNode -> HytaleTheme.accentColor
104+
is AssetNode.ZipNode -> HytaleTheme.warningColor
105+
is AssetNode.FolderNode -> HytaleTheme.textPrimary
106+
is AssetNode.FileNode -> getFileColor(data)
107+
else -> HytaleTheme.textPrimary
108+
}
109+
background = null
110+
isOpaque = false
111+
}
112+
113+
return this
114+
}
115+
116+
/**
117+
* Get the appropriate color for a file node based on its type.
118+
*/
119+
private fun getFileColor(file: AssetNode.FileNode): java.awt.Color {
120+
return when (file.assetType) {
121+
AssetType.TEXTURE -> HytaleTheme.successColor
122+
AssetType.SOUND -> HytaleTheme.warningColor
123+
AssetType.MODEL, AssetType.ANIMATION -> HytaleTheme.purpleAccent
124+
AssetType.DATA, AssetType.CONFIG -> HytaleTheme.accentColor
125+
else -> HytaleTheme.textPrimary
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)