feat(mpp-idea): migrate DevInEditorInput functionality to mpp-idea module#23
feat(mpp-idea): migrate DevInEditorInput functionality to mpp-idea module#23
Conversation
Deleted obsolete IdeaInputSection to clean up the codebase.
- Move model selector to left side of toolbar - Add MCP settings and prompt optimization buttons to right side - Create IdeaDevInEditorInput component with hybrid Swing/Compose UI - Add AutoAwesome icon for prompt optimization - Initialize PromptEnhancer and ConfigManager integration - Add placeholders for dialogs (to be implemented)
- Add auto-completion trigger for @, /, $, : characters - Try to use DevIn file type if available for syntax highlighting - Integrate with AutoPopupController for programmatic completion - Add PsiDocumentManager for document-PSI synchronization - Fall back to plain text if DevIn language is not available
- Create IdeaMcpConfigDialog with two tabs: Tools and MCP Servers - Implement auto-save functionality with 2 seconds delay - Add real-time JSON validation for MCP server configuration - Support incremental MCP server loading with progress indication - Use Jewel UI components (DefaultButton, OutlinedButton, Checkbox, etc.) - Integrate with IdeaDevInEditorInput component
- Create IdeaPromptOptimizationDialog with side-by-side comparison - Display original and enhanced prompts - Auto-enhance on dialog open using PromptEnhancer - Allow editing of enhanced text before applying - Integrate with IdeaDevInEditorInput component - Support Ctrl+P keyboard shortcut (via toolbar button)
|
Caution Review failedThe pull request is closed. Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughRefactors IDE editor UI: removes IdeaInputSection, updates bottom/top toolbars, adds MCP config and prompt-optimization dialogs, integrates DevIn PSI-backed documents and AutoPopup, swaps kotlinx-serialization for Gson in remote client, adds new icons, and updates Gradle/IDE plugin configuration for DevIn support. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (6)
Comment |
| val scope = rememberCoroutineScope() | ||
|
|
||
| // Create the input component | ||
| val inputComponent = remember { |
There was a problem hiding this comment.
To avoid leaks, consider registering inputComponent with the parent disposable via Disposer.register(disposable, inputComponent) so its dispose() runs when the parent is disposed (preventing dangling document listeners/message bus connections). (Guideline: no_memory_leaks)
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (8)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt (4)
36-42: Consider logging serialization failures.The function silently returns
"{}"on serialization errors, which may hide configuration issues during debugging. Consider logging the exception for troubleshooting purposes.private fun serializeMcpConfig(servers: Map<String, McpServerConfig>): String { return try { json.encodeToString(servers) } catch (e: Exception) { + println("⚠️ Failed to serialize MCP config: ${e.message}") "{}" } }
114-164: Redundant nestedscope.launchinLaunchedEffect.The inner
scope.launchon line 121 is redundant sinceLaunchedEffectalready provides a coroutine scope. This creates an extra coroutine unnecessarily.LaunchedEffect(Unit) { - scope.launch { try { toolConfig = ConfigManager.loadToolConfig() mcpConfigJson = serializeMcpConfig(toolConfig.mcpServers) if (toolConfig.mcpServers.isNotEmpty()) { - scope.launch { + // No need for nested launch - already in coroutine scope // Create callback for incremental loading val callback = object : McpLoadingStateCallback { // ... callback implementation } // ... rest of loading logic - } } isLoading = false } catch (e: Exception) { // ... error handling } - } }
276-294: Duplicate callback object definition.The
McpLoadingStateCallbackimplementation is duplicated here (lines 276-289) and in the initial loading (lines 123-140). Consider extracting to a helper function to reduce code duplication.// Extract to a helper function outside the composable or at the top of the function: fun createLoadingCallback( onStateUpdate: (McpLoadingState) -> Unit, onToolsUpdate: (String, List<ToolItem>) -> Unit ): McpLoadingStateCallback = object : McpLoadingStateCallback { override fun onServerStateChanged(serverName: String, state: McpServerState) { // ... implementation } override fun onLoadingStateChanged(loadingState: McpLoadingState) { onStateUpdate(loadingState) } override fun onBuiltinToolsLoaded(tools: List<ToolItem>) { // ... implementation } }
416-425: JSON editor lacks visual styling.The
BasicTextFieldfor MCP server JSON configuration lacks visual boundaries (e.g., background, border), making it hard to distinguish from surrounding content and potentially confusing for users.BasicTextField( state = textFieldState, - modifier = Modifier.fillMaxWidth().weight(1f), + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .background(JewelTheme.globalColors.panelBackground) + .padding(8.dp), textStyle = TextStyle( fontFamily = androidx.compose.ui.text.font.FontFamily.Monospace, color = org.jetbrains.jewel.foundation.theme.JewelTheme.globalColors.text.normal ), cursorBrush = SolidColor(org.jetbrains.jewel.foundation.theme.JewelTheme.globalColors.text.normal) )mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.kt (2)
43-43: Unused variablescope.The
rememberCoroutineScope()result is assigned toscopebut never used, asLaunchedEffectprovides its own scope.- val scope = rememberCoroutineScope()
108-118: Inconsistent BasicTextField API usage.The original text field uses the
value/onValueChangepattern while the enhanced text field uses thestatepattern. For consistency and future maintainability, consider using the same approach for both.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (2)
110-110: Inline fully-qualified class name should be imported.
cc.unitmesh.indexer.DomainDictServiceis referenced inline. Consider adding it to the imports for consistency and readability.+import cc.unitmesh.indexer.DomainDictService // ... -val domainDictService = cc.unitmesh.indexer.DomainDictService(fileSystem) +val domainDictService = DomainDictService(fileSystem)
104-112: Non-null assertion after null check could use safer Kotlin idioms.Using
currentModelConfig!!after checkingcurrentModelConfig != nullworks but is less idiomatic. Consider usingletor a local variable.- if (currentModelConfig != null && currentModelConfig!!.isValid()) { - llmService = KoogLLMService.create(currentModelConfig!!) + currentModelConfig?.takeIf { it.isValid() }?.let { config -> + llmService = KoogLLMService.create(config) val workspace = WorkspaceManager.currentWorkspace if (workspace != null) { val fileSystem = workspace.fileSystem - val domainDictService = cc.unitmesh.indexer.DomainDictService(fileSystem) - promptEnhancer = PromptEnhancer(llmService!!, fileSystem, domainDictService) + val domainDictService = DomainDictService(fileSystem) + promptEnhancer = PromptEnhancer(llmService!!, fileSystem, domainDictService) } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt(5 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInInput.kt(5 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaInputSection.kt(0 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt(1 hunks)
💤 Files with no reviewable changes (1)
- mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaInputSection.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
Use
expect/actualfor platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInInput.kt
🧬 Code graph analysis (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt (1)
IdeaModelSelector(33-130)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (1)
IdeaBottomToolbar(30-239)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt (1)
IdeaMcpConfigDialog(64-328)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.kt (1)
IdeaPromptOptimizationDialog(32-185)
🔇 Additional comments (7)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt (1)
1258-1304: LGTM!The
AutoAwesomeicon follows the established pattern in this file: lazy initialization, consistent 24dp dimensions, and standard path-based vector definition. The three-star design appropriately conveys AI/optimization functionality.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (2)
39-39: LGTM!The new
onPromptOptimizationClickcallback parameter is properly added with a default empty lambda for backward compatibility. The toolbar layout reorganization aligns with the PR objectives.
177-188: LGTM!The new Prompt Optimization button is well-integrated with appropriate sizing, accessibility description, and the new
AutoAwesomeicon. Placement alongside MCP Settings on the right side is logical.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInInput.kt (3)
57-64: LGTM!The lazy DevIn file type detection with safe fallback is appropriate for optional language support. Broad exception handling is acceptable here since the feature gracefully degrades.
209-224: LGTM!The document recreation logic properly handles DevIn language support with appropriate fallbacks. Using
ReadAction.computeensures thread safety, and the PSI-based document creation enables syntax highlighting and completion.
256-270: LGTM!The auto-completion trigger for special characters (
@,/,$,:) enhances UX by automatically showing relevant completions. Proper document commit before triggering the popup ensures PSI synchronization.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (1)
58-68: LGTM!The
IdeaDevInInputcomponent is properly created withrememberfor Compose state management, and correctly wired with the project and disposable for IntelliJ lifecycle integration.
| inputComponent.clearInput() | ||
| } | ||
| }, | ||
| sendEnabled = inputComponent.text.isNotBlank(), |
There was a problem hiding this comment.
sendEnabled won't update reactively.
inputComponent.text.isNotBlank() is evaluated at composition time but won't trigger recomposition when the Swing text changes. The Send button's enabled state won't update as the user types.
Consider tracking the input text in Compose state and updating it from the IdeaInputListener.onTextChanged callback:
+ var inputText by remember { mutableStateOf(initialText) }
+
// Add submit listener
DisposableEffect(Unit) {
val listener = object : IdeaInputListener {
// ...
override fun onTextChanged(text: String) {
- // Handle text changes if needed
+ inputText = text
}
// ...
}
// ...
}
// In IdeaBottomToolbar:
- sendEnabled = inputComponent.text.isNotBlank(),
+ sendEnabled = inputText.isNotBlank(),Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt
around line 143, the sendEnabled value is computed from
inputComponent.text.isNotBlank() at composition time and therefore won't update
on text changes; fix by introducing a Compose mutableStateOf<String> (e.g.
inputText) to hold the current text, initialize it from inputComponent.text,
update that state inside the IdeaInputListener.onTextChanged callback, and then
use inputText.isNotBlank() for sendEnabled so Compose recomposes correctly when
the user types; ensure the listener updates are posted to the UI thread if
necessary and remove/unregister the listener when the composable is disposed.
| // Model Configuration Dialog | ||
| if (showModelConfigDialog) { | ||
| // TODO: Create IDEA version of model configuration dialog | ||
| // For now, just close it | ||
| showModelConfigDialog = false | ||
| } |
There was a problem hiding this comment.
Model configuration dialog placeholder immediately dismisses.
The TODO placeholder immediately sets showModelConfigDialog = false, causing the dialog to never appear. This creates a confusing user experience where clicking "Configure Model..." has no visible effect.
Consider showing a placeholder message or disabling the configure option until implemented:
if (showModelConfigDialog) {
- // TODO: Create IDEA version of model configuration dialog
- // For now, just close it
- showModelConfigDialog = false
+ Dialog(onDismissRequest = { showModelConfigDialog = false }) {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text("Model Configuration")
+ Spacer(modifier = Modifier.height(8.dp))
+ Text("Coming soon - use Settings to configure models")
+ Spacer(modifier = Modifier.height(16.dp))
+ DefaultButton(onClick = { showModelConfigDialog = false }) {
+ Text("OK")
+ }
+ }
+ }
}Committable suggestion skipped: line range outside the PR's diff.
| isEnhancing = true | ||
| errorMessage = null | ||
| try { | ||
| val enhanced = enhancer.enhance(originalText.trim(), "zh") |
There was a problem hiding this comment.
Hardcoded language code "zh" limits internationalization.
The language is hardcoded to "zh" (Chinese), which may not be appropriate for all users. Consider making this configurable or detecting the user's locale.
+// Consider adding a parameter or detecting system locale
+val userLanguage = java.util.Locale.getDefault().language
val enhanced = enhancer.enhance(originalText.trim(), "zh")
+// Or pass as parameter: enhancer.enhance(originalText.trim(), userLanguage)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.kt
around line 51, the enhancer call uses a hardcoded language code "zh"; replace
this with a configurable or locale-driven value. Fetch the user's preferred
language from plugin settings or detect Locale.getDefault().language,
validate/normalize the code (fallback to "en" if unsupported), and pass that
variable into enhancer.enhance(originalText.trim(), languageCode) so the UI
respects user locale or configuration.
| horizontalArrangement = Arrangement.SpaceBetween, | ||
| verticalAlignment = Alignment.CenterVertically | ||
| ) { | ||
| Text("Prompt Optimization (Ctrl+P)") |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for Ctrl+P shortcut registration and related prompt optimization shortcuts
rg -n -i "ctrl.*p|prompt.*shortcut|VK_P" --type=kt -A 3 -B 3Repository: phodal/auto-dev-sketch
Length of output: 92
🏁 Script executed:
#!/bin/bash
# Search for Ctrl+P and shortcut-related patterns without type restriction
rg -n "Ctrl.*P|ctrl.*p|VK_P|vk_p|KeyEvent.VK_P" --max-count=20Repository: phodal/auto-dev-sketch
Length of output: 2813
🏁 Script executed:
#!/bin/bash
# Find and examine the IdeaPromptOptimizationDialog.kt file
find . -name "IdeaPromptOptimizationDialog.kt" -type fRepository: phodal/auto-dev-sketch
Length of output: 155
🏁 Script executed:
#!/bin/bash
# Search for prompt optimization related shortcuts and actions
rg -n "PromptOptimization|prompt.*optimization" -i | head -30Repository: phodal/auto-dev-sketch
Length of output: 3040
🏁 Script executed:
#!/bin/bash
# Read the IdeaPromptOptimizationDialog.kt file
cat -n ./mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.ktRepository: phodal/auto-dev-sketch
Length of output: 8466
🏁 Script executed:
#!/bin/bash
# Check IdeaDevInEditorInput.kt to see how the dialog is used and if shortcuts are handled there
cat -n ./mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt | head -200Repository: phodal/auto-dev-sketch
Length of output: 8559
Remove "(Ctrl+P)" from dialog header or implement the shortcut for IntelliJ IDEA.
The Ctrl+P shortcut is not registered for the IntelliJ IDEA plugin dialog. While Ctrl+P is implemented in the shared UI code (mpp-ui), the IDEA-specific dialog at mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.kt:85 is opened only via button click (line 157–158 in IdeaDevInEditorInput.kt). Either remove the "(Ctrl+P)" text to avoid misleading users, or register the keyboard shortcut in the IDEA editor context.
🤖 Prompt for AI Agents
In
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.kt
around line 85, the dialog header contains the misleading text "(Ctrl+P)";
either remove that text from the header or register the Ctrl+P shortcut to
actually open the dialog. To fix: simplest approach—remove the "(Ctrl+P)"
substring from the Text(...) at line 85. If you prefer to keep the hint,
implement the shortcut by exposing an IntelliJ action that opens this dialog (or
reuse the existing opener at IdeaDevInEditorInput.kt lines 157–158), register
that action in plugin.xml (or create a DumbAware AnAction) and bind the Ctrl+P
ShortcutSet (or registerCustomShortcutSet on the editor component) so pressing
Ctrl+P opens the dialog in the editor context.
- Add SmartToy icon for model selector (robot/AI icon) - Make model selector blend with background using hover effects - Remove workspace/project name display (not needed in IDEA) - Add border and rounded corners to IdeaDevInEditorInput - Add padding to editor panel for better spacing - Simplify token display to compact format (e.g., '1234t') - Reduce toolbar padding for cleaner look
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (3)
63-73: EnsureIdeaDevInInputis properly disposed to avoid leaks.
inputComponentis created and remembered but never explicitly tied to the passeddisposable. IfIdeaDevInInputregisters document listeners / message-bus subscriptions, this risks leaks when the parent is disposed.Consider registering it with the parent disposable (or otherwise wiring its lifecycle) if it implements
Disposable, e.g.:val inputComponent = remember { IdeaDevInInput( project = project, listeners = emptyList(), disposable = disposable, showAgent = true ).apply { text = initialText // If IdeaDevInInput is Disposable: // Disposer.register(disposable, this) } }
75-97: Send button enablement is not reactive to text changes.
sendEnabled = inputComponent.text.isNotBlank()is evaluated during composition, but Compose isn’t observinginputComponent.text, so typing in the Swing editor won’t automatically recompose and update the enabled state.Track the text in Compose state and update it from
IdeaInputListener.onTextChanged:var inputText by remember { mutableStateOf(initialText) } DisposableEffect(Unit) { val listener = object : IdeaInputListener { override fun onSubmit(text: String, trigger: IdeaInputTrigger) { onSubmit(text) inputComponent.clearInput() inputText = "" // keep state in sync } override fun onTextChanged(text: String) { inputText = text } override fun editorAdded(editor: com.intellij.openapi.editor.ex.EditorEx) { } } inputComponent.addInputListener(listener) onDispose { inputComponent.removeInputListener(listener) } } IdeaBottomToolbar( onSendClick = { val text = inputComponent.text.trim() if (text.isNotEmpty()) { onSubmit(text) inputComponent.clearInput() inputText = "" } }, sendEnabled = inputText.isNotBlank(), // ... )Ensure programmatic updates (e.g., from prompt optimization) also drive
inputTextviaonTextChangedor explicit state updates.Also applies to: 150-160
192-197: Model config dialog flag is immediately reset, so UI never appears.When
showModelConfigDialogbecomestrue, this branch runs once and immediately sets it back tofalse, with no UI. From the user’s perspective, clicking “Configure” appears to do nothing.If the real dialog isn’t ready yet, show a minimal placeholder dialog or disable the configure action until implemented. For example:
if (showModelConfigDialog) { Dialog(onDismissRequest = { showModelConfigDialog = false }) { Column(Modifier.padding(16.dp)) { Text("Model configuration is not yet available in this build.") Spacer(Modifier.height(16.dp)) DefaultButton(onClick = { showModelConfigDialog = false }) { Text("OK") } } } }
🧹 Nitpick comments (7)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt (2)
46-73: Hover interaction wiring looks solid; consider sharinginteractionSourcewithclickableThe hover-based background and
expandedcoupling are clean and keep the selector visually lightweight. One small improvement: you can pass the sameinteractionSourceintoclickableso hover/press states share a single source and you avoid creating an extra one under the hood.For example:
Row( modifier = Modifier .clip(RoundedCornerShape(6.dp)) .hoverable(interactionSource = interactionSource) .background( if (isHovered || expanded) JewelTheme.globalColors.borders.normal.copy(alpha = 0.3f) else Color.Transparent ) .clickable( interactionSource = interactionSource, indication = null, // or keep default if you want ripple ) { expanded = true } .padding(horizontal = 8.dp, vertical = 6.dp), ... )Purely optional, but keeps interaction state centralized.
74-93: Handle long model names more gracefully in the selector labelThe inline label composition looks good, but with
maxLines = 1and no width/overflow control, very long model names may either stretch the toolbar or get hard-clipped.You might want to constrain and ellipsize the text, for example:
Text( text = displayText, style = JewelTheme.defaultTextStyle.copy( fontSize = 12.sp, color = JewelTheme.globalColors.text.normal ), maxLines = 1, modifier = Modifier.weight(1f), overflow = TextOverflow.Ellipsis, )This keeps the selector compact while still signaling truncated content.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (3)
24-30: KDoc matches the new layout; consider slightly clarifying the right‑side descriptionThe KDoc correctly documents the new layout, but the bullet “Right side: MCP and prompt optimization” omits the
@,/, and send/stop actions.If you want it to be fully explicit, a minor tweak like this would help:
- * - Right side: MCP and prompt optimization + * - Right side: action triggers (@, /), MCP settings, prompt optimization, send/stop
49-80: Left‑side layout and token indicator look solid; consider making the token display a bit clearerThe model selector integration and subtle token text (
"${totalTokens}t") are implemented cleanly and match the described layout.If you expect non‑expert users, you might consider a slightly clearer label or a tooltip (e.g., “123 tokens”) instead of the bare
"123t", but this is purely UX polish.
82-139: Right‑side actions and prompt optimization button are wired correctly; optional enable/disable controlThe right‑side cluster wiring (MCP settings + new prompt optimization button) looks correct, with appropriate
contentDescriptions and consistent icon sizing.If there are cases where prompt optimization should be disabled (e.g., empty input or optimization already running), you might want to add a dedicated
promptOptimizationEnabledflag, similar tosendEnabled, and thread it through:fun IdeaBottomToolbar( onSendClick: () -> Unit, sendEnabled: Boolean, @@ - onSettingsClick: () -> Unit = {}, - onPromptOptimizationClick: () -> Unit = {}, + onSettingsClick: () -> Unit = {}, + onPromptOptimizationClick: () -> Unit = {}, + promptOptimizationEnabled: Boolean = true, @@ - IconButton( - onClick = onPromptOptimizationClick, - modifier = Modifier.size(32.dp) - ) { + IconButton( + onClick = onPromptOptimizationClick, + enabled = promptOptimizationEnabled, + modifier = Modifier.size(32.dp) + ) {Totally optional, depending on how the dialog is used.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (2)
37-48:initialText/placeholderare effectively write‑only props.
initialTextis applied once in therememberblock, andplaceholderis currently unused. If callers expect these parameters to be honored when they change over time (e.g., loading a draft or showing contextual placeholders), the current wiring won’t update the Swing component.If dynamic updates are desired, consider:
- Passing
placeholderthrough toIdeaDevInInput(if it supports it), and- Syncing
initialTextvia a side effect keyed on it:LaunchedEffect(initialText) { if (inputComponent.text != initialText) { inputComponent.text = initialText } }Otherwise, consider documenting that they are only used at first composition or trimming unused parameters.
Also applies to: 63-73
206-217: Guard prompt optimization when no enhancer is available.
IdeaPromptOptimizationDialogis always opened whenshowPromptOptimizationDialogis true, withenhancer = promptEnhancer. IfpromptEnhancerisnull(e.g., config load failed, invalid model, or no workspace), the dialog implementation must handle that gracefully.If
IdeaPromptOptimizationDialogdoes not support anullenhancer, consider:
- Disabling or hiding the prompt optimization button until
promptEnhanceris non-null, or- Showing a message in place of optimization (e.g., “Configure a valid model to enable prompt optimization”).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt(4 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt(4 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt(0 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt(1 hunks)
💤 Files with no reviewable changes (1)
- mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
Use
expect/actualfor platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build (223)
- GitHub Check: Build (241)
🔇 Additional comments (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt (1)
1258-1362: LGTM! Clean icon additions following established patterns.Both
SmartToyandAutoAwesomeicons are well-implemented and consistent with the existing icon definitions in this file. They correctly use lazy initialization, standard 24×24 dp dimensions, appropriate path definitions with helpful comments, and proper KDoc documentation.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (1)
32-48: NewonPromptOptimizationClickcallback is a clean, backwards‑friendly API extensionAdding
onPromptOptimizationClick: () -> Unit = {}with a default no‑op is a reasonable way to expose the new action without breaking existing Kotlin call sites, and the parameter ordering matches the visual button order.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (1)
99-123: Cannot verify the specific code at the mentioned lines as the file is not present in the repository.The original review identifies a legitimate architectural issue: if active model configuration changes are handled in
onConfigSelect(lines 177-185) without calling a function to reinitializellmServiceandpromptEnhancer, then configuration changes would not be reflected in the runtime behavior. The suggested fix—extracting the initialization logic into a reusable function called both on load and on config change—is a sound pattern for Kotlin/Compose applications.However, without access to the actual code at the referenced lines (99-123 and 177-185), I cannot:
- Confirm whether
onConfigSelectactually lacks the reinitialization logic- Verify if there's alternative handling elsewhere (e.g., through observers, reactive properties, or other mechanisms)
- Determine the full scope of the issue
- Create IdeaTopToolbar component with @ and / triggers moved from bottom - Add file selection buttons (clipboard, save, cursor, add file) - Add file chips display with remove functionality - Add new icons: ContentPaste, Save, TextFields, InsertDriveFile, Close - Simplify IdeaBottomToolbar by removing @ and / buttons - Update IdeaDevInEditorInput to use new top toolbar layout - Update IdeaAgentApp to match new toolbar API
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (3)
64-73: Register inputComponent with parent disposable to prevent leaks.The
inputComponentis created but not explicitly registered with the parent disposable, which could lead to resource leaks ifIdeaDevInInputholds document listeners or message bus connections.Consider registering with:
val inputComponent = remember { IdeaDevInInput( project = project, listeners = emptyList(), disposable = disposable, showAgent = true ).apply { text = initialText Disposer.register(disposable, this) } }Based on past review comment.
184-184: sendEnabled won't update reactively when user types.
inputComponent.text.isNotBlank()is evaluated at composition time but won't trigger recomposition when the Swing text changes. The Send button's enabled state will remain stale.Track the input text in Compose state and update it from the
IdeaInputListener.onTextChangedcallback:+ var inputText by remember { mutableStateOf(initialText) } + // Add submit listener DisposableEffect(Unit) { val listener = object : IdeaInputListener { // ... override fun onTextChanged(text: String) { - // Handle text changes if needed + inputText = text } // ... } // ... } // In IdeaBottomToolbar: - sendEnabled = inputComponent.text.isNotBlank(), + sendEnabled = inputText.isNotBlank(),Based on past review comment.
204-208: Model configuration dialog immediately dismisses.The TODO placeholder sets
showModelConfigDialog = falseimmediately, so clicking "Configure Model..." has no visible effect.Consider showing a placeholder message or temporarily disabling the configure option:
if (showModelConfigDialog) { - // TODO: Create IDEA version of model configuration dialog - // For now, just close it - showModelConfigDialog = false + org.jetbrains.jewel.ui.component.Dialog( + onDismissRequest = { showModelConfigDialog = false } + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text("Model Configuration") + Spacer(modifier = Modifier.height(8.dp)) + Text("Coming soon - use Settings to configure models") + Spacer(modifier = Modifier.height(16.dp)) + org.jetbrains.jewel.ui.component.DefaultButton( + onClick = { showModelConfigDialog = false } + ) { + Text("OK") + } + } + } }Based on past review comment.
🧹 Nitpick comments (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (2)
108-118: Prefer idiomatic null handling over !! operator.The null check followed by
!!is safe but not idiomatic. Consider using?.letfor cleaner, more Kotlin-style code.- // Initialize LLM service and prompt enhancer - if (currentModelConfig != null && currentModelConfig!!.isValid()) { - llmService = KoogLLMService.create(currentModelConfig!!) - - val workspace = WorkspaceManager.currentWorkspace - if (workspace != null) { - val fileSystem = workspace.fileSystem - val domainDictService = cc.unitmesh.indexer.DomainDictService(fileSystem) - promptEnhancer = PromptEnhancer(llmService!!, fileSystem, domainDictService) - } - } + // Initialize LLM service and prompt enhancer + currentModelConfig?.takeIf { it.isValid() }?.let { config -> + llmService = KoogLLMService.create(config) + + WorkspaceManager.currentWorkspace?.let { workspace -> + val fileSystem = workspace.fileSystem + val domainDictService = cc.unitmesh.indexer.DomainDictService(fileSystem) + promptEnhancer = PromptEnhancer(llmService!!, fileSystem, domainDictService) + } + }
141-154: Toolbar actions have placeholder implementations.Several toolbar actions (clipboard paste, save to workspace, insert current selection, file picker) are marked with TODO comments and not yet implemented.
These incomplete features may affect user experience. Do you want me to open an issue to track implementing these actions, or would you like help generating the implementation code?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt(3 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt(0 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt(1 hunks)
💤 Files with no reviewable changes (1)
- mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
Use
expect/actualfor platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt
🧬 Code graph analysis (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.kt (4)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt (1)
IdeaTopToolbar(30-122)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (1)
IdeaBottomToolbar(30-159)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.kt (1)
IdeaMcpConfigDialog(64-328)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.kt (1)
IdeaPromptOptimizationDialog(32-185)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build (241)
- GitHub Check: Build (223)
🔇 Additional comments (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt (1)
1258-1542: LGTM! Clean icon additions.The seven new icon definitions (SmartToy, AutoAwesome, ContentPaste, Save, TextFields, InsertDriveFile, Close) follow the established pattern and are implemented consistently with existing icons in this file.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaTopToolbar.kt (1)
1-189: LGTM! Well-structured toolbar implementation.The toolbar implementation follows Compose best practices with proper hover state management, tooltips, and clean separation of concerns. The conditional separator and hover-to-show close button provide good UX.
Deleted IdeaDevInEditorInput.kt as it is no longer needed.
- Use IdeaVerticalResizableSplitPane to separate timeline content and input area - Allow dynamic resizing of input area (30% to 90% of available space) - Initial split ratio set to 75% for content, 25% for input - Apply resizable layout to CODING, LOCAL_CHAT, and REMOTE agent types - CODE_REVIEW and KNOWLEDGE modes use fixed layout (no input area)
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (1)
325-345: Stale closure bug:isProcessingwon't update inside SwingPanel factory.The
isProcessingparameter is captured when the SwingPanel factory lambda executes (once on creation). Subsequent recompositions with updatedisProcessingvalues won't affect the listener's!isProcessingcheck on line 331, allowing users to triggeronSendeven while processing.Use
rememberUpdatedStateto ensure the listener sees the current value:private fun IdeaDevInInputArea( ... isProcessing: Boolean, ... ) { var inputText by remember { mutableStateOf("") } var devInInput by remember { mutableStateOf<IdeaDevInInput?>(null) } + val currentIsProcessing by rememberUpdatedState(isProcessing) + val currentOnSend by rememberUpdatedState(onSend) + val currentOnAbort by rememberUpdatedState(onAbort) Column( ... addInputListener(object : IdeaInputListener { ... override fun onSubmit(text: String, trigger: IdeaInputTrigger) { - if (text.isNotBlank() && !isProcessing) { - onSend(text) + if (text.isNotBlank() && !currentIsProcessing) { + currentOnSend(text) clearInput() inputText = "" } } override fun onStop() { - onAbort() + currentOnAbort() }
🧹 Nitpick comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (1)
297-304: Remove unused parametersworkspacePathandonAtClick.These parameters are declared but never used within the function body. If the functionality they represent has been intentionally removed (as suggested by the AI summary regarding disconnected shortcuts), the parameters should be cleaned up to avoid confusion.
private fun IdeaDevInInputArea( project: Project, parentDisposable: Disposable, isProcessing: Boolean, onSend: (String) -> Unit, onAbort: () -> Unit, - workspacePath: String? = null, totalTokens: Int? = null, onSettingsClick: () -> Unit = {}, - onAtClick: () -> Unit = {}, availableConfigs: List<NamedModelConfig> = emptyList(), currentConfigName: String? = null, onConfigSelect: (NamedModelConfig) -> Unit = {}, onConfigureClick: () -> Unit = {} )Also update the call sites at lines 174, 177, 220, and 223 to remove these arguments.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
Use
expect/actualfor platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt
🧬 Code graph analysis (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/IdeaVerticalResizableSplitPane.kt (1)
IdeaVerticalResizableSplitPane(36-140)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/timeline/IdeaTimelineContent.kt (2)
IdeaTimelineContent(21-50)IdeaEmptyStateMessage(94-108)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/remote/IdeaRemoteAgentContent.kt (2)
IdeaRemoteAgentContent(30-96)getEffectiveProjectId(313-324)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build (241)
- GitHub Check: Build (223)
🔇 Additional comments (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (3)
151-187: LGTM on the split pane integration for CODING/LOCAL_CHAT.The resizable split pane setup with timeline content above and input area below is well-structured. The 75% initial ratio provides a reasonable default for viewing conversation history.
188-234: LGTM on the REMOTE agent split pane integration.The remote agent implementation correctly handles the project ID derivation and includes appropriate error messaging when the project identifier is blank.
365-383: LGTM on toolbar integration.The toolbar correctly uses the
isProcessingparameter forsendEnabled(which updates on recomposition), and theonSendClickhandler sensibly reads from the editor directly withinputTextas fallback.
- Change SwingPanel from fixed height (120.dp) to weight(1f) for dynamic sizing - Change Column modifier from fillMaxWidth to fillMaxSize - Remove fixed preferredSize from JPanel wrapper - EditorTextField now properly resizes when dragging the split pane divider
|
augment review |
| mcpServers = newMcpServers | ||
| ) | ||
|
|
||
| ConfigManager.saveToolConfig(updatedConfig) |
There was a problem hiding this comment.
ConfigManager.saveToolConfig(updatedConfig) is executed from a Compose scope (likely EDT); file I/O and JSON work on the UI thread can freeze the IDE—consider offloading auto-save to a background dispatcher or IntelliJ background task to avoid EDT violations. (Guideline: edt_violations, background_task_handling)
🤖 Was this useful? React with 👍 or 👎
|
|
||
| try { | ||
| // Use incremental loading | ||
| mcpLoadingState = McpToolConfigManager.discoverMcpToolsIncremental( |
There was a problem hiding this comment.
McpToolConfigManager.discoverMcpToolsIncremental(...) may perform network/disk operations and is launched on the Compose scope (EDT); running this on the UI thread risks UI stalls—consider moving it to a background context. (Also applies to the reload call below.) (Guideline: edt_violations, background_task_handling)
🤖 Was this useful? React with 👍 or 👎
| 0 -> McpToolsTab( | ||
| mcpTools = mcpTools, | ||
| mcpLoadingState = mcpLoadingState, | ||
| onToolToggle = { toolName, enabled -> |
There was a problem hiding this comment.
In onToolToggle, toggling by tool.name updates all servers with the same tool name; this can unintentionally flip multiple entries. Consider scoping toggles by server (e.g., include serverName) to avoid cross‑server side effects.
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (2)
118-134:DisposableEffectdisposes the wrong ViewModels and leaks the active oneWhen
DisposableEffect(currentAgentType)detects a key change, theonDisposelambda executes with the previouscurrentAgentTypevalue (the value captured when the effect was created), not the new one. This causes:
- The ViewModel for the departing tab is never disposed (because the condition is false)
- The ViewModels for other tabs—including the one being switched to—are disposed (because conditions are true)
- Result: resource leak and a freshly-created ViewModel is immediately torn down before it can be used
The suggested fix is correct: unconditionally dispose all ViewModels in
onDispose, allowing theLaunchedEffectbelow to handle lazy creation with its null checks. This ensures proper cleanup on each agent-type change and on final teardown.
291-385: The input listener'sisProcessingcheck is stale and allows double-sends via Enter keyIn
IdeaDevInInputArea, theIdeaInputListener.onSubmitregistered in theSwingPanelfactory capturesisProcessingat factory time. BecauseSwingPanel.factoryruns exactly once and is never re-run on recomposition, this listener will always use the originalisProcessingvalue, even after the parameter changes. This means:
- Pressing Enter while an execution is in progress will still send, if the Swing component was created when
isProcessingwasfalse.- This diverges from the bottom toolbar, where
sendEnabledcorrectly disables the button whenisProcessingchanges.Fix by using
rememberUpdatedStateto capture the latest value:val isProcessingState = rememberUpdatedState(isProcessing) // In listener: if (text.isNotBlank() && !isProcessingState.value) {
🧹 Nitpick comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (1)
151-251: Split-pane layout is solid; consider gating REMOTE sends on connectivityThe new
IdeaVerticalResizableSplitPaneusage for CODING/LOCAL_CHAT and REMOTE reads clean and matches the existing split-pane patterns (e.g., code review). The timeline/input separation, ratios, and weight usage look good.One improvement for the REMOTE branch: you already collect
remoteIsConnectedbut do not use it when sending. Today the user can submit tasks while disconnected and only gets a “project or Git URL” error, not a connectivity hint.You could gate sending on connectivity and surface a clearer message:
IdeaDevInInputArea( project = project, parentDisposable = viewModel, isProcessing = remoteIsExecuting, onSend = { task -> - val effectiveProjectId = getEffectiveProjectId(remoteProjectId, remoteGitUrl) - if (effectiveProjectId.isNotBlank()) { - remoteVm.executeTask(effectiveProjectId, task, remoteGitUrl) - } else { - remoteVm.renderer.renderError("Please provide a project or Git URL") - } + if (!remoteIsConnected) { + remoteVm.renderer.renderError("Please connect to the remote agent server first") + return@IdeaDevInInputArea + } + + val effectiveProjectId = getEffectiveProjectId(remoteProjectId, remoteGitUrl) + if (effectiveProjectId.isNotBlank()) { + remoteVm.executeTask(effectiveProjectId, task, remoteGitUrl) + } else { + remoteVm.renderer.renderError("Please provide a project ID or Git URL") + } },This keeps the new layout intact while tightening the REMOTE UX.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt(4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
Use
expect/actualfor platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt
🧬 Code graph analysis (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (3)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/components/IdeaVerticalResizableSplitPane.kt (1)
IdeaVerticalResizableSplitPane(36-140)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/remote/IdeaRemoteAgentContent.kt (2)
IdeaRemoteAgentContent(30-96)getEffectiveProjectId(313-324)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/codereview/IdeaCodeReviewContent.kt (1)
IdeaCodeReviewContent(20-77)
🔇 Additional comments (2)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (2)
19-19: Import ofIdeaVerticalResizableSplitPanelooks correctThe new import aligns with the component definition under
cc.unitmesh.devins.idea.componentsand its usage below; no issues here.
310-317: SwingPanel sizing and layout integration look goodThe changes to wrap
IdeaDevInInputin aJPanelwith a minimalDimension(200, 60)and to let it fill viaModifier.fillMaxWidth().weight(1f)integrate well with the split-pane layout and avoid hardcoded preferred sizes. This should behave nicely under resizing.Also applies to: 352-357
…input - Add alignment = BottomStart to Popup to make it appear above the selector - This prevents the dropdown from covering the input area when opened
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt (4)
20-20: Redundant import alias.Line 13 already imports
androidx.compose.ui.Alignment, and line 20 creates an aliasComposeAlignmentfor the same type. This alias is only used once (line 100), while the unqualifiedAlignmentis used elsewhere (lines 72, 185). Remove the alias and use the unqualifiedAlignmentthroughout for consistency.Apply this diff to remove the redundant alias:
-import androidx.compose.ui.Alignment as ComposeAlignmentThen update line 100:
- alignment = ComposeAlignment.BottomStart, + alignment = Alignment.BottomStart,
64-69: Consider using background color tokens instead of border color.Using
JewelTheme.globalColors.borders.normalfor the hover background is semantically incorrect. Border colors are typically distinct from background/overlay colors in design systems. Consider using an appropriate background or overlay color token to ensure the component remains theme-consistent.Also, simplify the transparent color on line 68:
- androidx.compose.ui.graphics.Color.Transparent + Color.Transparent
62-94: Consider extracting design values into theme tokens.Multiple hardcoded values appear throughout the component (sizes: 6.dp, 8.dp, 4.dp, 14.dp; alphas: 0.7f, 0.6f; fontSize: 12.sp). While functional, extracting these into theme tokens or constants would improve maintainability and design consistency across the codebase.
75-80: Consider adding content description for the SmartToy icon.The SmartToy icon provides semantic meaning (AI/model indicator) but has
contentDescription = null. While the adjacent text provides context, adding a description like "Model" would improve accessibility for screen reader users.Icon( imageVector = IdeaComposeIcons.SmartToy, - contentDescription = null, + contentDescription = "Model", tint = JewelTheme.globalColors.text.normal.copy(alpha = 0.7f), modifier = Modifier.size(14.dp) )
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt
📄 CodeRabbit inference engine (AGENTS.md)
Use
expect/actualfor platform-specific code in KMP projects (e.g., file I/O on JVM/JS/Wasm)
Files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt
🧠 Learnings (1)
📚 Learning: 2025-11-30T02:30:49.805Z
Learnt from: CR
Repo: phodal/auto-dev-sketch PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-30T02:30:49.805Z
Learning: Applies to **/src/{androidMain,desktopMain}/**/*.kt : For Compose (Desktop/Android), use `AutoDevColors` from `cc.unitmesh.devins.ui.compose.theme` or `MaterialTheme.colorScheme`
Applied to files:
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt
🔇 Additional comments (1)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt (1)
158-215: LGTM!The
IdeaDropdownMenuItemhelper is well-implemented with proper state handling for enabled/disabled/selected states, conditional styling, and appropriate use of modifiers.
| // Dropdown popup - positioned above the selector to avoid covering input area | ||
| if (expanded) { | ||
| Popup( | ||
| alignment = ComposeAlignment.BottomStart, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -type f -name "IdeaModelSelector.kt" | head -5Repository: phodal/auto-dev-sketch
Length of output: 144
🏁 Script executed:
find . -type f -name "IdeaModelSelector.kt" -exec cat -n {} \; | sed -n '85,115p'Repository: phodal/auto-dev-sketch
Length of output: 1581
🏁 Script executed:
find . -path "*/mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt" 2>/dev/nullRepository: phodal/auto-dev-sketch
Length of output: 144
🏁 Script executed:
fd "IdeaModelSelector.kt" 2>/dev/nullRepository: phodal/auto-dev-sketch
Length of output: 142
🏁 Script executed:
ls -la mpp-idea/ 2>/dev/null || echo "Directory not found"Repository: phodal/auto-dev-sketch
Length of output: 370
🏁 Script executed:
git ls-files | grep -i "IdeaModelSelector" | head -5Repository: phodal/auto-dev-sketch
Length of output: 142
🏁 Script executed:
rg -l "ComposeAlignment" --type=kt 2>/dev/null | head -10Repository: phodal/auto-dev-sketch
Length of output: 48
🏁 Script executed:
rg "IdeaModelSelector" --type=kt -l 2>/dev/null | head -5Repository: phodal/auto-dev-sketch
Length of output: 48
🏁 Script executed:
git ls-files "*.kt" | head -20Repository: phodal/auto-dev-sketch
Length of output: 1395
🏁 Script executed:
rg -nP --type=kt "Popup\s*\(" -A 10 | grep -E "(Popup|alignment|Alignment)" | head -30Repository: phodal/auto-dev-sketch
Length of output: 92
🌐 Web query:
ComposeAlignment BottomStart popup positioning behavior
💡 Result:
Short answer: Popup alignment (e.g. Alignment.BottomStart) positions the popup relative to its anchor by computing an anchor point and a popup point and placing the popup so those points coincide, then applying the IntOffset (respecting LTR/RTL). Concretely the code computes:
position = anchorBounds.topLeft + anchorAlignmentPoint + popupAlignmentPoint + resolvedUserOffset.
- anchorAlignmentPoint is where the chosen Alignment maps inside the anchor bounds.
- popupAlignmentPoint is the negative of where the same Alignment maps inside the popup content (so the two alignment points line up).
- resolvedUserOffset applies X flipped in RTL, Y unchanged. (android.googlesource.com)
Implication for Alignment.BottomStart:
- the anchor point is the anchor's bottom‑start corner; the popup point is the popup's bottom‑start corner (so the popup is placed with its bottom‑start at the anchor's bottom‑start), then offset is applied. In LTR start = left; in RTL start = right. (composables.com)
If you need popup to appear adjacent (outside) the anchor or to auto‑flip when there’s not enough space, use a custom PopupPositionProvider or one of the position providers (e.g., for dropdowns/context menus). The PopupPositionProvider API exposes anchorBounds and windowSize to compute alternative placements. (developer.android.com)
If you want, tell me which behavior you’re seeing and I’ll help diagnose with specific code.
Citations:
- 1: https://android.googlesource.com/platform/frameworks/support/%2B/HEAD/compose/ui/ui/src/commonMain/kotlin/androidx/compose/ui/window/Popup.kt?utm_source=openai
- 2: https://composables.com/docs/androidx.compose.ui/ui/1.9.0-beta03/composable-functions/Popup?utm_source=openai
- 3: https://developer.android.com/reference/kotlin/androidx/compose/ui/window/PopupPositionProvider?utm_source=openai
🏁 Script executed:
rg "BottomStart|TopStart|positioned.*above|positioned.*below" --type=kt 2>/dev/null | head -20Repository: phodal/auto-dev-sketch
Length of output: 48
🏁 Script executed:
rg "Dropdown popup|positioned above|avoid covering input" --type=kt 2>/dev/nullRepository: phodal/auto-dev-sketch
Length of output: 48
🏁 Script executed:
git ls-files | grep "\.kt$" | wc -lRepository: phodal/auto-dev-sketch
Length of output: 70
Comment contradicts popup alignment.
Line 97 states the popup is "positioned above the selector," but ComposeAlignment.BottomStart on line 100 places the popup below the anchor (its bottom-start corner aligns with the anchor's bottom-start corner). To actually position above as intended, use ComposeAlignment.TopStart or provide a custom PopupPositionProvider with upward offset logic.
🤖 Prompt for AI Agents
In mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt
around lines 97 to 100, the comment says the dropdown popup should be
"positioned above the selector" but the code uses ComposeAlignment.BottomStart
which places the popup below; change the alignment to ComposeAlignment.TopStart
to align the popup's top-start to the anchor (placing it above), or if more
precise control is required implement and pass a PopupPositionProvider that
calculates an upward offset from the anchor to ensure the popup sits above the
selector even when space constraints occur.
There was a problem hiding this comment.
Pull request overview
This PR migrates the complete DevInEditorInput functionality from the mpp-ui module to the mpp-idea module, introducing IntelliJ IDEA-specific integrations and UI reorganization. The changes replace the previous Compose-only input with a hybrid Swing/Compose solution that leverages IntelliJ's native editor infrastructure for better DevIn language support.
Key Changes:
- Integrated resizable split panes for chat-based agent modes (CODING, LOCAL_CHAT, REMOTE)
- Created new dialogs for prompt optimization and MCP configuration with auto-save
- Enhanced DevIn input with auto-completion triggers and dynamic file type detection
- Reorganized toolbar layout with model selector moved to left, MCP settings and prompt optimization to right
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| IdeaComposeIcons.kt | Added 6 new Material Design icons (SmartToy, AutoAwesome, ContentPaste, Save, TextFields, InsertDriveFile, Close) for toolbar UI |
| IdeaAgentApp.kt | Replaced fixed Box layout with IdeaVerticalResizableSplitPane for CODING/LOCAL_CHAT/REMOTE modes; moved input area into bottom pane |
| IdeaTopToolbar.kt | NEW - Created top toolbar with @ / triggers and file selection (not yet integrated into UI) |
| IdeaPromptOptimizationDialog.kt | NEW - Side-by-side prompt enhancement dialog with editable output (hardcoded language, missing integration) |
| IdeaModelSelector.kt | Redesigned with transparent background, hover effects, and SmartToy icon; popup alignment issue |
| IdeaMcpConfigDialog.kt | NEW - Two-tab configuration dialog with auto-save and incremental loading (uses println instead of Logger) |
| IdeaInputSection.kt | DELETED - Removed pure Compose input in favor of Swing-based DevIn editor |
| IdeaDevInInput.kt | Added DevIn file type detection, PsiDocumentManager integration, and auto-completion triggers (trigger logic bug) |
| IdeaBottomToolbar.kt | Removed @ / buttons and workspace indicator; added prompt optimization button (not connected) |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * Layout: @ - / - Clipboard - Save - Cursor | Selected Files... | Add | ||
| */ | ||
| @Composable | ||
| fun IdeaTopToolbar( |
There was a problem hiding this comment.
The IdeaTopToolbar component is defined but never integrated into the UI. According to the comment in IdeaBottomToolbar, the @ and / triggers were moved to this toolbar, but it's not being used anywhere in IdeaDevInInputArea or the main application flow.
| IconButton(onClick = onDismiss) { | ||
| Text("×") | ||
| } |
There was a problem hiding this comment.
The close button uses a text "×" symbol without proper accessibility description. Consider using an actual Icon component with a proper contentDescription parameter (e.g., contentDescription = "Close dialog") to improve accessibility for screen readers.
| isEnhancing = true | ||
| errorMessage = null | ||
| try { | ||
| val enhanced = enhancer.enhance(originalText.trim(), "zh") |
There was a problem hiding this comment.
The language parameter is hardcoded to "zh" (Chinese). This should either be configurable, derived from the IDE's locale settings, or default to a more neutral language like "en". Hardcoding "zh" may produce unexpected results for non-Chinese users.
| import androidx.compose.ui.text.font.FontFamily | ||
| import androidx.compose.ui.unit.dp | ||
| import androidx.compose.ui.window.Dialog | ||
| import cc.unitmesh.llm.KoogLLMService |
There was a problem hiding this comment.
Unused import: KoogLLMService is imported but not used in this file. Consider removing it to keep the imports clean.
| import cc.unitmesh.llm.KoogLLMService |
| println("✅ Auto-saved tool configuration") | ||
| } | ||
| } catch (e: Exception) { | ||
| println("❌ Auto-save failed: ${e.message}") |
There was a problem hiding this comment.
Debug print statements should be replaced with proper logging using IntelliJ's logging framework (e.g., Logger.getInstance(IdeaMcpConfigDialog::class.java)). This ensures consistent logging behavior and allows users to control log levels through IDE settings.
| println("❌ Error loading MCP tools: ${e.message}") | ||
| } | ||
| } | ||
| } | ||
|
|
||
| isLoading = false | ||
| } catch (e: Exception) { | ||
| println("Error loading tool config: ${e.message}") |
There was a problem hiding this comment.
Debug print statements should be replaced with proper logging using IntelliJ's logging framework (e.g., Logger.getInstance(IdeaMcpConfigDialog::class.java)). This ensures consistent logging behavior and allows users to control log levels through IDE settings.
| onSlashClick: () -> Unit = {}, | ||
| onSettingsClick: () -> Unit = {}, | ||
| workspacePath: String? = null, | ||
| onPromptOptimizationClick: () -> Unit = {}, |
There was a problem hiding this comment.
The onPromptOptimizationClick parameter in IdeaBottomToolbar is not connected to any functionality. The prompt optimization dialog (IdeaPromptOptimizationDialog) is not instantiated or triggered anywhere in the codebase, making this button non-functional.
| // Dropdown popup - positioned above the selector to avoid covering input area | ||
| if (expanded) { | ||
| Popup( | ||
| alignment = ComposeAlignment.BottomStart, |
There was a problem hiding this comment.
The popup alignment is set to BottomStart, which positions the popup below the selector. However, the comment on line 97 states the popup should be "positioned above the selector to avoid covering input area." The alignment should be TopStart to match the intended behavior.
| alignment = ComposeAlignment.BottomStart, | |
| alignment = ComposeAlignment.TopStart, |
| IconButton(onClick = onDismiss) { | ||
| Text("×") | ||
| } |
There was a problem hiding this comment.
The close button uses a text "×" symbol without proper accessibility description. Consider using an actual Icon component with a proper contentDescription parameter (e.g., contentDescription = "Close dialog") to improve accessibility for screen readers.
| currentEditor.caretModel.moveToOffset(document.textLength) | ||
|
|
||
| // Auto-trigger completion for special characters | ||
| if (textToAppend in listOf("@", "/", "$", ":")) { |
There was a problem hiding this comment.
The auto-trigger completion logic checks if the entire textToAppend string matches a trigger character, but if multiple characters are appended at once (e.g., "abc@"), the completion won't trigger. Consider checking if textToAppend contains or ends with a trigger character instead: if (textToAppend.lastOrNull() in listOf('@', '/', '$', ':')).
| if (textToAppend in listOf("@", "/", "$", ":")) { | |
| if (textToAppend.lastOrNull() in listOf('@', '/', '$', ':')) { |
Integrates DevIn language for completion and syntax highlighting, replaces kotlinx.serialization with Gson for JSON handling, updates dependencies, and improves Gradle configuration.
Define required extension points in plugin.xml to support devins-lang integration.
Summary
Migrate the complete functionality of
DevInEditorInputfrommpp-uimodule tompp-ideamodule, with layout reorganization and IDEA-specific integrations.Changes
1. Create IdeaDevInEditorInput Component
IdeaDevInInput(Swing) withIdeaBottomToolbar(Compose)ConfigManagerandPromptEnhancerintegration2. IDEA Completion System Integration
AutoPopupControllerto auto-trigger completion on@,/,$,:charactersPsiDocumentManagerfor document-PSI synchronization3. Toolbar Layout Reorganization
AutoAwesomeicon for prompt optimization4. MCP Configuration Dialog
5. Prompt Optimization Dialog
PromptEnhancerFiles Changed
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInEditorInput.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaMcpConfigDialog.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaPromptOptimizationDialog.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaDevInInput.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.ktTesting
Pull Request opened by Augment Code with guidance from the PR author
Summary by CodeRabbit
New Features
UI Improvements
Removed
✏️ Tip: You can customize this high-level summary in your review settings.