-
Notifications
You must be signed in to change notification settings - Fork 1
feat(mpp-idea): enhance IdeaBottomToolbar with icons, slash command, and ModelSelector #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… command support - Add Send, Folder, AlternateEmail, ArrowDropDown icons to IdeaComposeIcons - Replace emoji/text with proper Jewel Icon components in IdeaBottomToolbar - Add onSlashClick callback for / command trigger button - Update IdeaInputSection and IdeaAgentApp to support slash commands - Align toolbar features with mpp-ui BottomToolbar (except ModelSelector)
WalkthroughAdds UI and wiring for model configuration and a slash-command trigger: new model selector component and config dialog, icons, toolbar/input callbacks for "/" insertion, and a ViewModel method to set the active config. Visual/icon changes replace prior text badges with Icon components. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant BottomToolbar
participant InputSection
participant TextFieldState
participant IdeaAgentViewModel
participant ConfigManager
participant ModelConfigDialog
User->>BottomToolbar: Clicks model selector / slash / configure
BottomToolbar->>InputSection: onSlashClick / onConfigClick / onConfigSelect
InputSection->>TextFieldState: edit { append("/") } %% slash path
InputSection->>IdeaAgentViewModel: onConfigSelect(name)
IdeaAgentViewModel->>ConfigManager: setActive(configName)
ConfigManager-->>IdeaAgentViewModel: reload config / result
IdeaAgentViewModel-->>InputSection: emit updated config state
BottomToolbar->>ModelConfigDialog: onConfigureClick -> show dialog
ModelConfigDialog->>IdeaAgentViewModel: onSave(name, config)
IdeaAgentViewModel->>ConfigManager: persist and setActive
ConfigManager-->>IdeaAgentViewModel: success / error
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review completed. No suggestions at this time.
Comment augment review to trigger a new review at any time.
- Add IdeaModelSelector for switching between LLM configurations - Add IdeaModelConfigDialog for configuring LLM models with provider, model name, API key, base URL, and advanced settings - Add new icons to IdeaComposeIcons: Check, Visibility, VisibilityOff, ExpandMore, ExpandLess - Integrate ModelSelector into IdeaBottomToolbar - Update IdeaAgentApp with model selector state management - Add setActiveConfig method to IdeaAgentViewModel - Remove duplicate ExpandMore/ExpandLess icons from IdeaComposeIcons
|
augment review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Review completed. 3 suggestions posted.
Comment augment review to trigger a new review at any time.
| }, | ||
| onSlashClick = { | ||
| // Insert / at current cursor position to trigger slash commands | ||
| devInInput?.appendText("/") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pressing the slash button inserts "/" here, but IdeaInputSection also appends "/" in its onSlashClick handler (see editor/IdeaInputSection.kt:130), leading to duplicate insertion; consider handling the insertion in one place.
🤖 Was this useful? React with 👍 or 👎
| horizontalArrangement = Arrangement.spacedBy(8.dp), | ||
| verticalAlignment = Alignment.CenterVertically | ||
| ) { | ||
| TextField( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
showApiKey is toggled but not applied to the API key field, so the key is always visible; consider masking/unmasking the TextField based on showApiKey.
🤖 Was this useful? React with 👍 or 👎
| val name = configName.ifEmpty { "${provider.name.lowercase()}-${modelName}" } | ||
| onSave(name, config) | ||
| }, | ||
| enabled = modelNameState.text.isNotEmpty() && apiKeyState.text.isNotEmpty() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Save is disabled when the API key is empty, but some providers (e.g., local/Ollama) may not require a key; consider enabling save when the selected provider doesn’t need credentials.
🤖 Was this useful? React with 👍 or 👎
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (7)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt (2)
18-18: Unused import.
ModelConfigis imported but not used in this file—onlyNamedModelConfigis used.-import cc.unitmesh.llm.ModelConfig
81-90: Consider adding a max height constraint to the dropdown.The dropdown Box uses
verticalScrollbut has noheightIn(max = ...)constraint. With many saved configurations, the popup could extend beyond screen bounds.Box( modifier = Modifier .widthIn(min = 200.dp, max = 300.dp) + .heightIn(max = 300.dp) .clip(RoundedCornerShape(8.dp)) .background(JewelTheme.globalColors.panelBackground) .padding(4.dp) ) {mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (1)
146-158: Consider using an icon for the slash button for visual consistency.The @ trigger uses an
Iconcomponent (AlternateEmail), but the / trigger uses aTextcomponent. For visual consistency across trigger buttons, consider using an icon or styled badge for the slash command trigger as well.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelConfigDialog.kt (4)
237-267: Save handler: numeric parsing silently normalizes invalid input
temperatureStateandmaxTokensStateare parsed withtoDoubleOrNull()/toIntOrNull()and then defaulted to0.0and128000(Lines 255–256). This is safe but can be surprising if the user types an invalid value and you quietly normalize it.Consider either:
- Validating and preventing save when parsing fails, or
- Clamping to a valid range (e.g., temperature 0.0–2.0) and surfacing the correction in the UI.
This would make misconfigurations easier to catch and debug.
99-117: Base URL behavior across providers may retain stale valuesYou:
- Auto-fill
baseUrlStatewithhttp://localhost:11434when selectingLLMProviderType.OLLAMAand the base URL is empty (Lines 105–113).- Compute
needsBaseUrlfrom a provider list to conditionally show the Base URL field (Lines 172–177).- Always persist
baseUrl = baseUrlState.text.toString()intoModelConfigregardless ofneedsBaseUrl(Line 257).This means a base URL entered (or auto-set) for one provider can remain hidden but still be passed along after switching to a provider that does not conceptually need it, depending on how
ModelConfigis consumed.Consider:
- Clearing
baseUrlStatewhen switching to providers that don’t need a base URL, or- Ignoring
baseUrldownstream for providers whereneedsBaseUrl == false.That will avoid surprising overrides based on stale configuration.
Also applies to: 172-186
295-364: Provider dropdown implementation looks solid; consider future filtering
IdeaProviderSelectorcleanly:
- Uses an
OutlinedButtonwith a trailing dropdown icon.- Renders all
LLMProviderType.entriesin a scrollable popup.- Marks the active provider with a check icon (Lines 349–355).
If the list of providers grows, you might later want to:
- Filter out experimental/unsupported providers here, or
- Add basic grouping or search.
For now, the implementation looks good.
370-446: Model selector UX is good; optional enhancement for long model lists
IdeaModelNameSelectorgives a solid experience:
- Free-form
TextFieldplus dropdown trigger.- Popup with scrollable list and a check icon for the current model (Lines 431–437).
If
availableModelscan become large, consider:
- Filtering the list based on the current
modelNameState.text, or- Highlighting matches to the typed prefix.
This would keep the dropdown usable with many models.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt(7 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelConfigDialog.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt(8 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentViewModel.kt(1 hunks)mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt(1 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.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.ktmpp-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/IdeaModelConfigDialog.ktmpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentViewModel.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/IdeaModelConfigDialog.kt (1)
mpp-web/src/services/ConfigService.ts (1)
ModelConfig(6-13)
⏰ 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 (10)
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaComposeIcons.kt (1)
515-755: LGTM! Well-structured icon additions.The new icons (Send, Folder, AlternateEmail, ArrowDropDown, Check, Visibility, VisibilityOff) follow the established pattern consistently: lazy initialization, 24x24 viewport, and SolidColor(Color.Black) fill. This maintains consistency with existing icons in the file.
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentViewModel.kt (1)
433-446: LGTM! Consistent implementation.The
setActiveConfigmethod follows the established pattern used bysaveModelConfig: coroutine launch, config operation, reload on success, and error handling viarenderer.renderError. Clean and consistent.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelSelector.kt (1)
33-130: Good structure for the model selector component.The component properly handles empty state, selection indication, and configure action. The use of
rememberwith appropriate keys forcurrentConfiganddisplayTextensures efficient recomposition.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaBottomToolbar.kt (2)
28-45: Well-designed API expansion.The new parameters (
onSlashClick, model configuration props) have sensible defaults maintaining backward compatibility. The KDoc accurately reflects the updated layout.
125-131: Good integration of the model selector.The
IdeaModelSelectoris properly wired with all required props passed through from the toolbar's parameters.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/toolwindow/IdeaAgentApp.kt (4)
66-72: Good use ofrememberfor derived state.Using
remember(configWrapper)ensuresavailableConfigsandcurrentConfigNameare only recomputed when the config wrapper changes, avoiding unnecessary recalculations during recomposition.
187-201: Clean dialog integration.The model configuration dialog is properly gated by
showConfigDialogstate. The save flow correctly creates aNamedModelConfig, saves it as active, and dismisses the dialog.
308-311: LGTM! Slash command trigger follows the established pattern.The implementation mirrors the
onAtClickhandler by appending the trigger character to the editor input.
217-231: Well-structured parameter additions.The new parameters have appropriate defaults and are properly forwarded to
IdeaBottomToolbar. The KDoc is updated to reflect the model selector feature.mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelConfigDialog.kt (1)
273-289: Form field wrapper is clean and consistent
IdeaConfigFormFieldnicely standardizes label styling and spacing across fields, improving consistency and reducing boilerplate.No changes needed here.
| // API Key | ||
| IdeaConfigFormField(label = "API Key") { | ||
| Row( | ||
| modifier = Modifier.fillMaxWidth(), | ||
| horizontalArrangement = Arrangement.spacedBy(8.dp), | ||
| verticalAlignment = Alignment.CenterVertically | ||
| ) { | ||
| TextField( | ||
| state = apiKeyState, | ||
| placeholder = { Text("Enter API key") }, | ||
| modifier = Modifier.weight(1f) | ||
| ) | ||
| IconButton( | ||
| onClick = { showApiKey = !showApiKey }, | ||
| modifier = Modifier.size(32.dp) | ||
| ) { | ||
| Icon( | ||
| imageVector = if (showApiKey) IdeaComposeIcons.VisibilityOff else IdeaComposeIcons.Visibility, | ||
| contentDescription = if (showApiKey) "Hide" else "Show", | ||
| tint = JewelTheme.globalColors.text.normal, | ||
| modifier = Modifier.size(18.dp) | ||
| ) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
org.jetbrains.jewel.ui.component.TextField password masking show hide toggle
💡 Result:
Short answer: use a boolean state to switch visualTransformation between PasswordVisualTransformation() and VisualTransformation.None and put an IconButton in the TextField's trailingIcon (or trailing) slot to toggle it. This works with Jewel's TextField because it follows Compose TextField APIs.
Example (Kotlin Compose / Jewel-style):
var text by remember { mutableStateOf("") }
var visible by remember { mutableStateOf(false) }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Password") },
visualTransformation = if (visible) VisualTransformation.None else PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
trailingIcon = {
IconButton(onClick = { visible = !visible }) {
Icon(
imageVector = if (visible) Icons.Default.VisibilityOff else Icons.Default.Visibility,
contentDescription = if (visible) "Hide password" else "Show password"
)
}
}
)
References:
- TextField API (trailingIcon, visualTransformation). [1]
- Android guide: show/hide password toggle (example & UX). [2]
- PasswordVisualTransformation docs. [3]
Sources:
[1] kotlinlang.org Compose TextField docs.
[2] developer.android.com "Show or hide password based on a user toggle".
[3] developer.android.com PasswordVisualTransformation API.
🏁 Script executed:
cat -n mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelConfigDialog.kt | head -200Repository: phodal/auto-dev-sketch
Length of output: 10361
🏁 Script executed:
head -33 mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelConfigDialog.kt | grep -E "import|PasswordVisualTransformation"Repository: phodal/auto-dev-sketch
Length of output: 1428
🏁 Script executed:
rg "PasswordVisualTransformation|visualTransformation" --type ktRepository: phodal/auto-dev-sketch
Length of output: 92
🏁 Script executed:
rg "TextField.*password|TextField.*secret|password.*TextField|secret.*TextField" --type kt -i -A 3Repository: phodal/auto-dev-sketch
Length of output: 92
🏁 Script executed:
rg "PasswordVisualTransformation|visualTransformation" -A 3 -B 1Repository: phodal/auto-dev-sketch
Length of output: 4902
🏁 Script executed:
rg "showPassword|hidePassword|maskPassword" -A 3 -B 1Repository: phodal/auto-dev-sketch
Length of output: 48
🏁 Script executed:
find . -name "*.kt" -type f | xargs grep -l "TextField" | head -5Repository: phodal/auto-dev-sketch
Length of output: 496
🏁 Script executed:
sed -n '1,50p' mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/editor/ModelConfigDialog.kt | head -40Repository: phodal/auto-dev-sketch
Length of output: 1657
🏁 Script executed:
sed -n '100,150p' mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/editor/ModelConfigDialog.ktRepository: phodal/auto-dev-sketch
Length of output: 2536
🏁 Script executed:
grep -n "API Key\|API key\|apiKey" mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/editor/ModelConfigDialog.kt -A 15 | head -35Repository: phodal/auto-dev-sketch
Length of output: 2129
Wire showApiKey toggle to apply password masking via visualTransformation
The showApiKey boolean toggles the icon between Visibility and VisibilityOff (lines 159–167), but the TextField always renders as plain text. The toggle has no effect on actual field visibility/masking.
Add the missing visualTransformation parameter to the TextField:
visualTransformation = if (showApiKey) VisualTransformation.None else PasswordVisualTransformation(),Also add the missing imports:
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformationA correct implementation of this pattern already exists in mpp-ui/src/commonMain/kotlin/cc/unitmesh/devins/ui/compose/editor/ModelConfigDialog.kt (line 251).
🤖 Prompt for AI Agents
In
mpp-idea/src/main/kotlin/cc/unitmesh/devins/idea/editor/IdeaModelConfigDialog.kt
around lines 146 to 170, the showApiKey toggle only changes the icon but does
not mask/unmask the TextField; wire the toggle into the TextField by adding
visualTransformation = if (showApiKey) VisualTransformation.None else
PasswordVisualTransformation(), and add the required imports
androidx.compose.ui.text.input.PasswordVisualTransformation and
androidx.compose.ui.text.input.VisualTransformation so the field actually
switches between masked and plain text.
Summary
Enhance
IdeaBottomToolbarto align withmpp-ui/BottomToolbarby adding proper icons, slash command support, and ModelSelector component.Changes
New Components
IdeaModelSelector.kt- Jewel-based dropdown for selecting LLM configurationsIdeaModelConfigDialog.kt- Dialog for configuring LLM models with:Icons Added to
IdeaComposeIcons.ktSend- paper plane icon for send buttonFolder- folder icon for workspace indicatorAlternateEmail- @ symbol icon for agent triggerArrowDropDown- dropdown arrowCheck- checkmark for selectionVisibility/VisibilityOff- eye icons for password toggleExpandMore/ExpandLess- chevron icons for collapsible sectionsIdeaBottomToolbar.ktUpdatesIconcomponentsonSlashClickcallback for/command trigger buttonavailableConfigs,currentConfigName,onConfigSelect,onConfigureClickparametersViewModel Updates
IdeaAgentViewModel.kt: AddsetActiveConfig(configName)method to switch active LLM configurationCaller Updates
IdeaAgentApp.kt:onSlashClickhandlerFeature Comparison
Testing
./gradlew :mpp-idea:compileKotlinpassesPull Request opened by Augment Code with guidance from the PR author
Summary by CodeRabbit
New Features
UI Improvements
✏️ Tip: You can customize this high-level summary in your review settings.