diff --git a/plugin-playground/src/main/kotlin/KondoKit/.DS_Store b/plugin-playground/src/main/kotlin/KondoKit/.DS_Store index 14cbc7b..0956fb2 100644 Binary files a/plugin-playground/src/main/kotlin/KondoKit/.DS_Store and b/plugin-playground/src/main/kotlin/KondoKit/.DS_Store differ diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcher.kt b/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcher.kt index 5b04203..1743517 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcher.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcher.kt @@ -11,54 +11,83 @@ import java.net.URL import javax.swing.SwingUtilities object GitLabPluginFetcher { - private const val GITLAB_ACCESS_TOKEN = "glpat-dE2Cs2e4b32-H7c9oGuS" private const val GITLAB_PROJECT_ID = "38297322" private const val GITLAB_BRANCH = "master" + private const val CHROME_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" + private const val DEBUG = true // Set to false to disable debug logging + + // Debug logging function + private fun debugLog(message: String) { + if (DEBUG) { + println("[GitLabPluginFetcher] $message") + } + } // Function to fetch plugins from GitLab fun fetchGitLabPlugins(onComplete: (List) -> Unit) { Thread { try { + debugLog("Starting to fetch GitLab plugins...") val plugins = mutableListOf() val apiUrl = "https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/repository/tree?ref=${GITLAB_BRANCH}" + debugLog("API URL: $apiUrl") // Create URL connection val url = URL(apiUrl) val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" - connection.setRequestProperty("PRIVATE-TOKEN", GITLAB_ACCESS_TOKEN) + connection.setRequestProperty("User-Agent", CHROME_USER_AGENT) + debugLog("Set User-Agent header to: $CHROME_USER_AGENT") // Read response val responseCode = connection.responseCode + debugLog("Response code: $responseCode") + if (responseCode == HttpURLConnection.HTTP_OK) { val reader = BufferedReader(InputStreamReader(connection.inputStream)) val response = reader.use { it.readText() } + debugLog("Response length: ${response.length} characters") + debugLog("Response preview: ${response.take(200)}...") // Parse JSON response val gson = Gson() val treeItems = gson.fromJson(response, Array::class.java) + debugLog("Parsed ${treeItems.size} items from JSON") // Filter for directories (trees) + var directoryCount = 0 for (jsonObject in treeItems) { if (jsonObject["type"].asString == "tree") { + directoryCount++ val folderId = jsonObject["id"].asString val folderPath = jsonObject["path"].asString + debugLog("Processing directory: $folderPath (ID: $folderId)") try { val pluginProperties = fetchPluginProperties(folderPath) plugins.add(GitLabPlugin(folderId, folderPath, pluginProperties, null)) + debugLog("Successfully fetched properties for: $folderPath") } catch (e: Exception) { + debugLog("Error fetching plugin.properties for $folderPath: ${e.message}") plugins.add(GitLabPlugin(folderId, folderPath, null, "Error fetching plugin.properties")) } } } + debugLog("Found $directoryCount directories (trees)") + } else { + debugLog("HTTP error: $responseCode") + val errorReader = BufferedReader(InputStreamReader(connection.errorStream)) + val errorResponse = errorReader.use { it.readText() } + debugLog("Error response: $errorResponse") } + debugLog("Completed fetching plugins. Total plugins: ${plugins.size}") // Update on UI thread SwingUtilities.invokeLater { onComplete(plugins) } } catch (e: Exception) { + debugLog("Exception occurred: ${e.message}") e.printStackTrace() SwingUtilities.invokeLater { onComplete(emptyList()) @@ -71,27 +100,37 @@ object GitLabPluginFetcher { private fun fetchPluginProperties(folderPath: String): PluginProperties { val pluginFilePath = "$folderPath/plugin.properties" val pluginUrl = "https://gitlab.com/api/v4/projects/${GITLAB_PROJECT_ID}/repository/files/${pluginFilePath.replace("/", "%2F")}/raw?ref=${GITLAB_BRANCH}" + debugLog("Fetching plugin.properties from: $pluginUrl") // Create URL connection val url = URL(pluginUrl) val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" - connection.setRequestProperty("PRIVATE-TOKEN", GITLAB_ACCESS_TOKEN) + connection.setRequestProperty("User-Agent", CHROME_USER_AGENT) // Read response val responseCode = connection.responseCode + debugLog("plugin.properties response code: $responseCode") + if (responseCode == HttpURLConnection.HTTP_OK) { val reader = BufferedReader(InputStreamReader(connection.inputStream)) val rawContent = reader.use { it.readText() } + debugLog("plugin.properties content length: ${rawContent.length} characters") + debugLog("plugin.properties content preview: ${rawContent.take(200)}...") return parseProperties(rawContent) } else { + debugLog("Failed to fetch plugin.properties. Response code: $responseCode") + val errorReader = BufferedReader(InputStreamReader(connection.errorStream)) + val errorResponse = errorReader.use { it.readText() } + debugLog("Error response for plugin.properties: $errorResponse") throw Exception("Plugin file not found for folder: $folderPath") } } // Function to parse plugin.properties content private fun parseProperties(content: String): PluginProperties { + debugLog("Parsing plugin.properties content") val lines = content.split("\n") var author = "Unknown" var version = "Unknown" @@ -104,13 +143,23 @@ object GitLabPluginFetcher { val value = parts[1].replace("\"", "").replace("'", "").trim() when { - key.startsWith("AUTHOR", ignoreCase = true) -> author = value - key.startsWith("VERSION", ignoreCase = true) -> version = value - key.startsWith("DESCRIPTION", ignoreCase = true) -> description = value + key.startsWith("AUTHOR", ignoreCase = true) -> { + author = value + debugLog("Found author: $value") + } + key.startsWith("VERSION", ignoreCase = true) -> { + version = value + debugLog("Found version: $value") + } + key.startsWith("DESCRIPTION", ignoreCase = true) -> { + description = value + debugLog("Found description: $value") + } } } } + debugLog("Parsed properties - Author: $author, Version: $version, Description: $description") return PluginProperties(author, version, description) } } \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcherTest.kt b/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcherTest.kt new file mode 100644 index 0000000..dbf5aea --- /dev/null +++ b/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/GitLabPluginFetcherTest.kt @@ -0,0 +1,19 @@ +package KondoKit.components.ReflectiveEditorComponents + +/** + * Simple test to verify GitLabPluginFetcher debug logging works + */ +fun main() { + println("Testing GitLabPluginFetcher with debug logging...") + + GitLabPluginFetcher.fetchGitLabPlugins { plugins -> + println("Fetched ${plugins.size} plugins") + plugins.forEach { plugin -> + println("- ${plugin.path}: ${plugin.pluginProperties?.description ?: plugin.pluginError}") + } + } + + // Give the async fetch some time to complete + Thread.sleep(10000) + println("Test completed") +} \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginStatus.kt b/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginStatus.kt new file mode 100644 index 0000000..4f90590 --- /dev/null +++ b/plugin-playground/src/main/kotlin/KondoKit/components/ReflectiveEditorComponents/PluginStatus.kt @@ -0,0 +1,15 @@ +package KondoKit.components.ReflectiveEditorComponents + +/** + * Data class representing a plugin with its installation status and update information + */ +data class PluginStatus( + val name: String, + val installedVersion: String?, + val remoteVersion: String?, + val description: String?, + val author: String?, + val gitLabPlugin: GitLabPlugin?, + val isInstalled: Boolean, + val needsUpdate: Boolean +) \ No newline at end of file diff --git a/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt b/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt index e31709f..b3d07f4 100644 --- a/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt +++ b/plugin-playground/src/main/kotlin/KondoKit/views/ReflectiveEditorView.kt @@ -5,6 +5,7 @@ import KondoKit.components.* import KondoKit.components.ReflectiveEditorComponents.CustomSearchField import KondoKit.components.ReflectiveEditorComponents.GitLabPlugin import KondoKit.components.ReflectiveEditorComponents.GitLabPluginFetcher +import KondoKit.components.ReflectiveEditorComponents.PluginStatus import KondoKit.Helpers.showToast import KondoKit.plugin import KondoKit.plugin.Companion.TITLE_BAR_COLOR @@ -42,6 +43,9 @@ object ReflectiveEditorView : View { // Store fetched GitLab plugins private var gitLabPlugins: List = listOf() + + // Store combined plugin status (installed + remote) + private var pluginStatuses: List = listOf() // Store the cog icon to be reused private var cogIcon: Icon? = null @@ -114,7 +118,10 @@ object ReflectiveEditorView : View { // Fetch GitLab plugins in the background GitLabPluginFetcher.fetchGitLabPlugins { plugins -> + System.out.println("GitLab plugins fetched: ${plugins.size}") gitLabPlugins = plugins + // Update plugin statuses with comparison between installed and remote plugins + updatePluginStatuses() // We'll update the UI when needed } } @@ -226,13 +233,22 @@ object ReflectiveEditorView : View { // Add a section for available plugins from GitLab that are not installed // Only show this section when there's a search term if (pluginSearchText.isNotBlank()) { - val matchingGitLabPlugins = gitLabPlugins.filter { plugin -> - plugin.pluginProperties != null && - (plugin.path.contains(pluginSearchText, ignoreCase = true) || - plugin.pluginProperties.description.contains(pluginSearchText, ignoreCase = true)) + System.out.println("Filtering plugins for search term: '$pluginSearchText'") + System.out.println("Total plugin statuses: ${pluginStatuses.size}") + + val matchingPluginStatuses = pluginStatuses.filter { pluginStatus -> + // Only show plugins that are not currently loaded + val shouldShow = !pluginStatus.isInstalled && + (pluginStatus.name.contains(pluginSearchText, ignoreCase = true) || + (pluginStatus.description?.contains(pluginSearchText, ignoreCase = true) ?: false)) + + System.out.println("Plugin: ${pluginStatus.name}, isInstalled: ${pluginStatus.isInstalled}, shouldShow: $shouldShow") + shouldShow } - if (matchingGitLabPlugins.isNotEmpty()) { + System.out.println("Matching plugin statuses: ${matchingPluginStatuses.size}") + + if (matchingPluginStatuses.isNotEmpty()) { // Add a separator val separator = JPanel() separator.background = VIEW_BACKGROUND_COLOR @@ -252,9 +268,10 @@ object ReflectiveEditorView : View { panel.add(headerPanel) panel.add(Box.createVerticalStrut(5)) - // Add matching GitLab plugins - for (gitLabPlugin in matchingGitLabPlugins) { - val pluginPanel = createGitLabPluginItemPanel(gitLabPlugin) + // Add matching plugin statuses + for (pluginStatus in matchingPluginStatuses) { + System.out.println("Adding plugin to UI: ${pluginStatus.name}") + val pluginPanel = createPluginStatusItemPanel(pluginStatus) panel.add(pluginPanel) panel.add(Box.createVerticalStrut(5)) } @@ -419,6 +436,72 @@ object ReflectiveEditorView : View { return panel } + private fun createPluginStatusItemPanel(pluginStatus: PluginStatus): JPanel { + val panel = JPanel(BorderLayout()) + panel.background = WIDGET_COLOR + panel.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) + panel.maximumSize = Dimension(220, 60) + + // Plugin name + val nameLabel = JLabel(pluginStatus.name) + nameLabel.foreground = secondaryColor + nameLabel.font = Font("RuneScape Small", Font.PLAIN, 16) + + // Action button based on plugin status + val actionButton = JButton() + actionButton.background = TITLE_BAR_COLOR + actionButton.foreground = secondaryColor + actionButton.font = Font("RuneScape Small", Font.PLAIN, 14) + + if (pluginStatus.isInstalled) { + if (pluginStatus.needsUpdate) { + actionButton.text = "Update" + actionButton.addActionListener { + // TODO: Implement update functionality + showToast(mainPanel, "Update functionality not yet implemented", JOptionPane.INFORMATION_MESSAGE) + } + } else { + actionButton.text = "Installed" + actionButton.isEnabled = false + } + } else { + actionButton.text = "Download" + actionButton.addActionListener { + // TODO: Implement download functionality + showToast(mainPanel, "Download functionality not yet implemented", JOptionPane.INFORMATION_MESSAGE) + } + } + + // Plugin toggle switch (iOS style) + val toggleSwitch = ToggleSwitch() + toggleSwitch.setActivated(pluginStatus.isInstalled && !pluginStatus.needsUpdate) + toggleSwitch.isEnabled = pluginStatus.isInstalled && !pluginStatus.needsUpdate + + if (pluginStatus.isInstalled && !pluginStatus.needsUpdate) { + toggleSwitch.onToggleListener = { activated -> + // TODO: Implement enable/disable functionality + showToast(mainPanel, "Enable/disable functionality not yet implemented", JOptionPane.INFORMATION_MESSAGE) + // Reset for now since functionality not implemented + toggleSwitch.setActivated(true) + } + } + + // Layout + val infoPanel = JPanel(BorderLayout()) + infoPanel.background = WIDGET_COLOR + infoPanel.add(nameLabel, BorderLayout.WEST) + + val controlsPanel = JPanel(FlowLayout(FlowLayout.RIGHT, 5, 0)) + controlsPanel.background = WIDGET_COLOR + controlsPanel.add(actionButton) + controlsPanel.add(toggleSwitch) + + panel.add(infoPanel, BorderLayout.CENTER) + panel.add(controlsPanel, BorderLayout.EAST) + + return panel + } + private fun enablePlugin(pluginName: String) { try { // Source and destination directories @@ -639,12 +722,16 @@ object ReflectiveEditorView : View { } fun addPlugins(reflectiveEditorView: JPanel) { + System.out.println("addPlugins called") // Ensure we run on the EDT; if not, reschedule and return if (!SwingUtilities.isEventDispatchThread()) { SwingUtilities.invokeLater { addPlugins(reflectiveEditorView) } return } + // Update plugin statuses to reflect current loaded plugins + updatePluginStatuses() + // Batch updates to avoid intermediate repaints/flicker mainPanel.ignoreRepaint = true try { @@ -755,4 +842,77 @@ object ReflectiveEditorView : View { } } } + + // Helper method to get currently loaded plugin names + private fun getLoadedPluginNames(): Set { + val loadedPluginNames = mutableSetOf() + + try { + // Get loaded plugins + val loadedPluginsField = PluginRepository::class.java.getDeclaredField("loadedPlugins") + loadedPluginsField.isAccessible = true + val loadedPlugins = loadedPluginsField.get(null) as HashMap<*, *> + + System.out.println("Found ${loadedPlugins.size} loaded plugins") + + for ((_, plugin) in loadedPlugins) { + val pluginName = getPluginDirName(plugin as Plugin) + loadedPluginNames.add(pluginName) + System.out.println("Loaded plugin: $pluginName") + } + } catch (e: Exception) { + e.printStackTrace() + } + + System.out.println("Returning loaded plugin names: ${loadedPluginNames.joinToString(", ")}") + return loadedPluginNames + } + + // Update plugin statuses by comparing installed and remote plugins + private fun updatePluginStatuses() { + val loadedPluginNames = getLoadedPluginNames() + val statuses = mutableListOf() + + System.out.println("Updating plugin statuses. Loaded plugins: ${loadedPluginNames.joinToString(", ")}") + + // Process remote plugins + for (gitLabPlugin in gitLabPlugins) { + val pluginName = gitLabPlugin.path + val remoteVersion = gitLabPlugin.pluginProperties?.version ?: "Unknown" + val description = gitLabPlugin.pluginProperties?.description ?: "No description available" + val author = gitLabPlugin.pluginProperties?.author ?: "Unknown" + + val isLoaded = loadedPluginNames.contains(pluginName) + System.out.println("Processing plugin: $pluginName, isLoaded: $isLoaded") + + if (isLoaded) { + // Plugin is currently loaded + statuses.add(PluginStatus( + name = pluginName, + installedVersion = remoteVersion, // We don't have the actual installed version, but we know it's loaded + remoteVersion = remoteVersion, + description = description, + author = author, + gitLabPlugin = gitLabPlugin, + isInstalled = true, + needsUpdate = false // We assume it's up-to-date since it's loaded + )) + } else { + // Plugin is not loaded + statuses.add(PluginStatus( + name = pluginName, + installedVersion = null, + remoteVersion = remoteVersion, + description = description, + author = author, + gitLabPlugin = gitLabPlugin, + isInstalled = false, + needsUpdate = false + )) + } + } + + System.out.println("Updated plugin statuses. Total statuses: ${statuses.size}") + pluginStatuses = statuses + } } \ No newline at end of file