Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.EditorFactory
import com.intellij.openapi.editor.EditorModificationUtil
import com.intellij.openapi.editor.actions.EnterAction
import com.intellij.openapi.editor.actions.IncrementalFindAction
Expand Down Expand Up @@ -181,7 +182,9 @@ class AutoDevInput(
val id = UUID.randomUUID()
val file = LightVirtualFile("AutoDevInput-$id", language, "")

val document = file.findDocument() ?: throw IllegalStateException("Can't create in-memory document")
val document = ReadAction.compute<Document, Throwable> {
EditorFactory.getInstance().createDocument("")
}

initializeDocumentListeners(document)
setDocument(document)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -585,13 +585,28 @@ class ToolOrchestrator(
context: cc.unitmesh.agent.tool.ToolExecutionContext
): ToolResult {
return try {
// Cast to ExecutableTool (all new tools should implement this)
// For SubAgent, we need to validate input first
if (tool is cc.unitmesh.agent.core.SubAgent<*, *>) {
@Suppress("UNCHECKED_CAST")
val subAgent = tool as cc.unitmesh.agent.core.SubAgent<Any, ToolResult>

// Validate and convert Map to proper input type
val validatedInput = subAgent.validateInput(params)

// Create invocation with validated input
val invocation = subAgent.createInvocation(validatedInput)

// Execute the tool
return invocation.execute(context)
}

// For other ExecutableTools, use params directly
@Suppress("UNCHECKED_CAST")
val executableTool = tool as? ExecutableTool<Any, ToolResult>
?: return ToolResult.Error("Tool ${tool.name} does not implement ExecutableTool interface")

val invocation = executableTool.createInvocation(params)

// Execute the tool
invocation.execute(context)
} catch (e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package cc.unitmesh.devins.idea.editor

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import cc.unitmesh.devins.ui.compose.theme.AutoDevColors
import org.jetbrains.jewel.foundation.theme.JewelTheme
import org.jetbrains.jewel.ui.component.*

/**
* Bottom toolbar for the input section.
* Provides send/stop buttons, @ trigger for agent completion, settings, and token info.
*
* Uses Jewel components for native IntelliJ IDEA look and feel.
*/
@Composable
fun IdeaBottomToolbar(
onSendClick: () -> Unit,
sendEnabled: Boolean,
isExecuting: Boolean = false,
onStopClick: () -> Unit = {},
onAtClick: () -> Unit = {},
onSettingsClick: () -> Unit = {},
workspacePath: String? = null,
totalTokens: Int? = null,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 6.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// Left side: workspace and token info
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.weight(1f, fill = false)
) {
// Workspace indicator
if (!workspacePath.isNullOrEmpty()) {
// Extract project name from path, handling both Unix and Windows separators
val projectName = workspacePath
.replace('\\', '/') // Normalize to Unix separator
.substringAfterLast('/')
.ifEmpty { "Project" }

Box(
modifier = Modifier
.background(
JewelTheme.globalColors.panelBackground.copy(alpha = 0.8f)
)
.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "📁",
style = JewelTheme.defaultTextStyle.copy(fontSize = 12.sp)
)
Text(
text = projectName,
style = JewelTheme.defaultTextStyle.copy(fontSize = 12.sp),
maxLines = 1
)
}
}
}

// Token usage indicator
if (totalTokens != null && totalTokens > 0) {
Box(
modifier = Modifier
.background(AutoDevColors.Blue.c400.copy(alpha = 0.2f))
.padding(horizontal = 8.dp, vertical = 4.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "Token",
style = JewelTheme.defaultTextStyle.copy(fontSize = 11.sp)
)
Text(
text = "$totalTokens",
style = JewelTheme.defaultTextStyle.copy(
fontSize = 11.sp,
fontWeight = FontWeight.Bold
)
)
}
}
}
}

// Right side: action buttons
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
// @ trigger button for agent completion
IconButton(
onClick = onAtClick,
modifier = Modifier.size(32.dp)
) {
Text(
text = "@",
style = JewelTheme.defaultTextStyle.copy(
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
)
}

// Settings button
IconButton(
onClick = onSettingsClick,
modifier = Modifier.size(32.dp)
) {
Text(
text = "⚙",
style = JewelTheme.defaultTextStyle.copy(fontSize = 14.sp)
)
}

// Send or Stop button
if (isExecuting) {
DefaultButton(
onClick = onStopClick,
modifier = Modifier.height(32.dp)
) {
Text(
text = "⏹ Stop",
style = JewelTheme.defaultTextStyle.copy(
color = AutoDevColors.Red.c400
)
)
}
} else {
DefaultButton(
onClick = onSendClick,
enabled = sendEnabled,
modifier = Modifier.height(32.dp)
) {
Text(
text = "Send",
style = JewelTheme.defaultTextStyle
)
}
}
}
}
}

Loading
Loading