Browse Source

Fixed clicking on referenced file not working in Intellij IDEA 2024.x

paviko 2 months ago
parent
commit
9781dee831

+ 1 - 0
hosts/jetbrains-plugin/changelog.html

@@ -4,6 +4,7 @@
 <ul>
 <ul>
   <li>Updated OpenCode to v1.0.121</li>
   <li>Updated OpenCode to v1.0.121</li>
   <li>Fixed working directory as server start directory, not git root</li>
   <li>Fixed working directory as server start directory, not git root</li>
+  <li>Fixed clicking on referenced file not working in Intellij IDEA 2024.x</li>
 </ul>
 </ul>
 
 
 <h3>2025.11.24</h3>
 <h3>2025.11.24</h3>

+ 36 - 39
hosts/jetbrains-plugin/src/main/kotlin/paviko/opencode/ui/ChatToolWindowFactory.kt

@@ -8,7 +8,6 @@ import com.intellij.openapi.wm.ToolWindow
 import com.intellij.openapi.wm.ToolWindowFactory
 import com.intellij.openapi.wm.ToolWindowFactory
 import com.intellij.ui.jcef.JBCefApp
 import com.intellij.ui.jcef.JBCefApp
 import com.intellij.ui.jcef.JBCefBrowser
 import com.intellij.ui.jcef.JBCefBrowser
-import com.intellij.ui.jcef.JBCefJSQuery
 import com.intellij.util.ui.JBUI
 import com.intellij.util.ui.JBUI
 import paviko.opencode.backendprocess.BackendLauncher
 import paviko.opencode.backendprocess.BackendLauncher
 import paviko.opencode.settings.OpenCodeSettings
 import paviko.opencode.settings.OpenCodeSettings
@@ -113,10 +112,24 @@ class ChatToolWindowFactory : ToolWindowFactory, DumbAware {
                                         // Store browser reference for path insertion (context actions)
                                         // Store browser reference for path insertion (context actions)
                                         // PathInserter.setBrowser(browser) - Removed, now stateless
                                         // PathInserter.setBrowser(browser) - Removed, now stateless
 
 
+                                        // Enable dropping files from the IDE onto the web UI via helper
+                                        try {
+                                            DragAndDropInstaller.install(project, browser, logger)
+                                        } catch (e: Exception) {
+                                            logger.warn("Failed to set up drag and drop", e)
+                                        }
+                                        
+                                        // Add browser to component hierarchy - required for JBCefJSQuery in IDEA 2024.3
+                                        mainPanel.removeAll()
+                                        mainPanel.add(browser.component, BorderLayout.CENTER)
+                                        // keep logs section at the bottom
+                                        mainPanel.add(hideableLogs, BorderLayout.SOUTH)
+                                        mainPanel.revalidate()
+                                        mainPanel.repaint()
 
 
+                                        // Install IdeBridge - uses CefLoadHandler internally to wait for browser ready
                                         IdeBridge.install(browser, project)
                                         IdeBridge.install(browser, project)
 
 
-
                                         // Push opened files and current file from IDE into the webview (@ overlay)
                                         // Push opened files and current file from IDE into the webview (@ overlay)
                                         try {
                                         try {
                                             val filesUpdater = IdeOpenFilesUpdater(project, browser)
                                             val filesUpdater = IdeOpenFilesUpdater(project, browser)
@@ -126,47 +139,31 @@ class ChatToolWindowFactory : ToolWindowFactory, DumbAware {
                                             logger.warn("Failed to install IdeOpenFilesUpdater", e)
                                             logger.warn("Failed to install IdeOpenFilesUpdater", e)
                                         }
                                         }
 
 
-                                        
                                         // Immediate attempt to enable tooltip polyfill (redundant with load handler)
                                         // Immediate attempt to enable tooltip polyfill (redundant with load handler)
-                                        SwingUtilities.invokeLater {
-                                            try {
-                                                val polyfillScriptEarly = """
-                                                    (function(){
-                                                        try { 
-                                                            document.documentElement.classList.add('tip-polyfill'); 
-                                                        } catch(e){}
-                                                        try { 
-                                                            if (window.__setTooltipPolyfill) {
-                                                                window.__setTooltipPolyfill(true);
-                                                            }
-                                                        } catch(e){}
-                                                    })();
-                                                """.trimIndent()
-                                                browser.cefBrowser.executeJavaScript(
-                                                    polyfillScriptEarly,
-                                                    browser.cefBrowser.url,
-                                                    0
-                                                )
-                                            } catch (e: Exception) {
-                                                logger.debug(
-                                                    "Early tooltip polyfill injection failed (will retry on load)",
-                                                    e
-                                                )
-                                            }
-                                        }
-
-                                        // Enable dropping files from the IDE onto the web UI via helper
                                         try {
                                         try {
-                                            DragAndDropInstaller.install(project, browser, logger)
+                                            val polyfillScriptEarly = """
+                                                (function(){
+                                                    try { 
+                                                        document.documentElement.classList.add('tip-polyfill'); 
+                                                    } catch(e){}
+                                                    try { 
+                                                        if (window.__setTooltipPolyfill) {
+                                                            window.__setTooltipPolyfill(true);
+                                                        }
+                                                    } catch(e){}
+                                                })();
+                                            """.trimIndent()
+                                            browser.cefBrowser.executeJavaScript(
+                                                polyfillScriptEarly,
+                                                browser.cefBrowser.url,
+                                                0
+                                            )
                                         } catch (e: Exception) {
                                         } catch (e: Exception) {
-                                            logger.warn("Failed to set up drag and drop", e)
+                                            logger.debug(
+                                                "Early tooltip polyfill injection failed (will retry on load)",
+                                                e
+                                            )
                                         }
                                         }
-                                        mainPanel.removeAll()
-                                        mainPanel.add(browser.component, BorderLayout.CENTER)
-                                        // keep logs section at the bottom
-                                        mainPanel.add(hideableLogs, BorderLayout.SOUTH)
-                                        mainPanel.revalidate()
-                                        mainPanel.repaint()
                                     } catch (e: Exception) {
                                     } catch (e: Exception) {
                                         logger.error("Failed to create browser component", e)
                                         logger.error("Failed to create browser component", e)
                                         mainPanel.removeAll()
                                         mainPanel.removeAll()

+ 47 - 2
hosts/jetbrains-plugin/src/main/kotlin/paviko/opencode/ui/IdeBridge.kt

@@ -12,7 +12,10 @@ import com.intellij.openapi.project.Project
 import com.intellij.openapi.vfs.LocalFileSystem
 import com.intellij.openapi.vfs.LocalFileSystem
 import com.intellij.ui.jcef.JBCefBrowser
 import com.intellij.ui.jcef.JBCefBrowser
 import com.intellij.ui.jcef.JBCefJSQuery
 import com.intellij.ui.jcef.JBCefJSQuery
+import org.cef.browser.CefBrowser
+import org.cef.handler.CefLoadHandlerAdapter
 import javax.swing.SwingUtilities
 import javax.swing.SwingUtilities
+import java.util.concurrent.atomic.AtomicBoolean
 
 
 object IdeBridge {
 object IdeBridge {
     private val logger = Logger.getInstance(IdeBridge::class.java)
     private val logger = Logger.getInstance(IdeBridge::class.java)
@@ -29,8 +32,50 @@ object IdeBridge {
     private val states = java.util.concurrent.ConcurrentHashMap<Project, ProjectState>()
     private val states = java.util.concurrent.ConcurrentHashMap<Project, ProjectState>()
 
 
     fun install(browser: JBCefBrowser, project: Project) {
     fun install(browser: JBCefBrowser, project: Project) {
-        val q = try { JBCefJSQuery.create(browser) } catch (_: Throwable) { null }
-        val state = ProjectState(browser, q)
+        // Create placeholder state without query - will be set up on page load
+        val state = ProjectState(browser, null)
+        states[project] = state
+        
+        val installed = AtomicBoolean(false)
+        
+        // Use load handler to ensure browser is fully ready before creating JBCefJSQuery
+        // This fixes race condition on cold IDE start in IDEA 2024.3
+        val loadHandler = object : CefLoadHandlerAdapter() {
+            override fun onLoadingStateChange(
+                cefBrowser: CefBrowser?,
+                isLoading: Boolean,
+                canGoBack: Boolean,
+                canGoForward: Boolean
+            ) {
+                // Install once when loading starts (browser is ready)
+                if (isLoading && installed.compareAndSet(false, true)) {
+                    SwingUtilities.invokeLater {
+                        doInstall(browser, project)
+                    }
+                }
+            }
+        }
+        
+        browser.jbCefClient.addLoadHandler(loadHandler, browser.cefBrowser)
+        
+        // Also try immediate install in case page already loading
+        SwingUtilities.invokeLater {
+            if (installed.compareAndSet(false, true)) {
+                doInstall(browser, project)
+            }
+        }
+    }
+    
+    private fun doInstall(browser: JBCefBrowser, project: Project) {
+        val q = try { JBCefJSQuery.create(browser) } catch (t: Throwable) { 
+            logger.warn("Failed to create JBCefJSQuery", t)
+            null 
+        }
+        
+        // Update state with query
+        val oldState = states[project]
+        val state = ProjectState(browser, q, oldState?.outbox ?: mutableListOf())
+        state.ready = oldState?.ready ?: false
         states[project] = state
         states[project] = state
         
         
         if (q != null) {
         if (q != null) {