Skip to content

Commit

Permalink
feat: add good ui with sender
Browse files Browse the repository at this point in the history
  • Loading branch information
phodal committed Aug 3, 2023
1 parent b81d619 commit 4874767
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 34 deletions.
3 changes: 3 additions & 0 deletions src/main/kotlin/cc/unitmesh/devti/AutoDevIcons.kt
Expand Up @@ -9,4 +9,7 @@ object AutoDevIcons {

@JvmField
val AI_COPILOT: Icon = IconLoader.getIcon("/icons/ai-copilot.svg", AutoDevIcons::class.java)

@JvmField
val Send: Icon = IconLoader.getIcon("/icons/send.svg", AutoDevIcons::class.java)
}
22 changes: 15 additions & 7 deletions src/main/kotlin/cc/unitmesh/devti/gui/chat/AutoDevInput.kt
Expand Up @@ -14,6 +14,7 @@ import com.intellij.openapi.editor.actions.EnterAction
import com.intellij.openapi.editor.actions.IncrementalFindAction
import com.intellij.openapi.editor.event.DocumentListener
import com.intellij.openapi.editor.ex.EditorEx
import com.intellij.openapi.fileEditor.impl.text.TextEditorProvider
import com.intellij.openapi.fileTypes.FileTypes
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
Expand All @@ -36,13 +37,14 @@ enum class AutoDevInputTrigger {

interface AutoDevInputListener : EventListener {
fun editorAdded(editor: EditorEx) {}
fun onSubmit(component: AutoDevInputField, trigger: AutoDevInputTrigger) {}
fun onSubmit(component: AutoDevInputSection, trigger: AutoDevInputTrigger) {}
}

class AutoDevInputField(
project: Project,
private val listeners: List<DocumentListener>,
val disposable: Disposable?,
val inputSection: AutoDevInputSection,
) : EditorTextField(project, FileTypes.PLAIN_TEXT), Disposable {
private val editorListeners: EventDispatcher<AutoDevInputListener> =
EventDispatcher.create(AutoDevInputListener::class.java)
Expand Down Expand Up @@ -88,7 +90,7 @@ class AutoDevInputField(
connect.subscribe(topic, object : AnActionListener {
override fun afterActionPerformed(action: AnAction, event: AnActionEvent, result: AnActionResult) {
if (event.dataContext.getData(CommonDataKeys.EDITOR) === this@AutoDevInputField.editor && action is EnterAction) {
editorListeners.multicaster.onSubmit(this@AutoDevInputField, AutoDevInputTrigger.Key)
editorListeners.multicaster.onSubmit(inputSection, AutoDevInputTrigger.Key)
}
}
})
Expand Down Expand Up @@ -119,14 +121,20 @@ class AutoDevInputField(
}

override fun getBackground(): Color {
val editor = editor
if (editor != null) {
val colorsScheme = editor.colorsScheme
return colorsScheme.defaultBackground
val editor = editor ?: return super.getBackground()
return editor.colorsScheme.defaultBackground
}

override fun getData(dataId: String): Any? {
if (!PlatformCoreDataKeys.FILE_EDITOR.`is`(dataId)) {
return super.getData(dataId)
}
return super.getBackground()

val it = editor ?: return super.getData(dataId)
return TextEditorProvider.getInstance().getTextEditor(it)
}


override fun dispose() {
listeners.forEach { editor?.document?.removeDocumentListener(it) }
}
Expand Down
78 changes: 51 additions & 27 deletions src/main/kotlin/cc/unitmesh/devti/gui/chat/AutoDevInputSection.kt
@@ -1,28 +1,31 @@
package cc.unitmesh.devti.gui.chat

import cc.unitmesh.devti.AutoDevBundle
import cc.unitmesh.devti.AutoDevIcons
import cc.unitmesh.devti.provider.ContextPrompter
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.Presentation
import com.intellij.openapi.actionSystem.impl.ActionButton
import com.intellij.openapi.editor.event.DocumentEvent
import com.intellij.openapi.editor.event.DocumentListener
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import com.intellij.openapi.wm.IdeFocusManager
import com.intellij.openapi.wm.impl.InternalDecorator
import com.intellij.ui.content.ContentManager
import com.intellij.util.EventDispatcher
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import com.intellij.util.ui.components.BorderLayoutPanel
import java.awt.BorderLayout
import java.awt.Color
import java.awt.Component
import java.awt.Dimension
import java.awt.event.ActionEvent
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
import javax.swing.Box
import javax.swing.JButton
import javax.swing.JComponent
import javax.swing.JPanel
import kotlin.math.max
import kotlin.math.min

Expand All @@ -34,13 +37,39 @@ class AutoDevInputSection(
) : BorderLayoutPanel() {
private val input: AutoDevInputField
private val documentListener: DocumentListener
private val buttonPresentation: Presentation
private val button: ActionButton
private val dispatcher: EventDispatcher<AutoDevInputListener> =
EventDispatcher.create(AutoDevInputListener::class.java)
var text: String
get() {
return input.text
}
set(text) {
input.recreateDocument()
input.text = text
}


init {
val presentation = Presentation(AutoDevBundle.message("devti.chat.send"))
val jButton = JButton(AutoDevBundle.message("devti.chat.send"))
presentation.icon = jButton.icon

input = AutoDevInputField(project, listOf(), disposable)
presentation.setIcon(AutoDevIcons.Send)
buttonPresentation = presentation
button = ActionButton(
DumbAwareAction.create {
object : DumbAwareAction("") {
override fun actionPerformed(e: AnActionEvent) {
dispatcher.multicaster.onSubmit(this@AutoDevInputSection, AutoDevInputTrigger.Button)
}
}.actionPerformed(it)
},
buttonPresentation,
"",
Dimension(20, 20)
)


input = AutoDevInputField(project, listOf(), disposable, this)
documentListener = object : DocumentListener {
override fun documentChanged(event: DocumentEvent) {
val i = input.preferredSize.height
Expand All @@ -53,24 +82,6 @@ class AutoDevInputSection(
input.addDocumentListener(documentListener)
input.recreateDocument()

val listener: (ActionEvent) -> Unit = {
val prompt = input.text
input.text = ""
val context = ChatContext(null, "", "")

chatCodingService.actionType = ChatActionType.CHAT
chatCodingService.handlePromptAndResponse(panel, object : ContextPrompter() {
override fun displayPrompt() = prompt
override fun requestPrompt() = prompt
}, context)
}

input.addListener(object : AutoDevInputListener {
override fun onSubmit(component: AutoDevInputField, trigger: AutoDevInputTrigger) {
listener.invoke(ActionEvent(component, 0, trigger.name))
}
})

addToCenter(input)

val borderLayoutPanel = BorderLayoutPanel()
Expand All @@ -81,9 +92,8 @@ class AutoDevInputSection(
ideFocusManager.requestFocus(input, true)
}
})

borderLayoutPanel.addToCenter(horizontalGlue)
borderLayoutPanel.addToRight(jButton)
borderLayoutPanel.addToRight(button)
addToBottom(borderLayoutPanel)
}

Expand All @@ -100,6 +110,20 @@ class AutoDevInputSection(
this.input.text = trimMargin
}

override fun getBackground(): Color? {
// it seems that the input field is not ready when this method is called
if (this.input == null) return super.getBackground()

val editor = input.editor ?: return super.getBackground()
return editor.colorsScheme.defaultBackground
}

override fun setBackground(bg: Color?) {}

fun addListener(listener: AutoDevInputListener) {
dispatcher.addListener(listener)
}

fun setSendingMode(sendingMode: Boolean) {
// input.setSendingMode(sendingMode)
}
Expand Down
14 changes: 14 additions & 0 deletions src/main/kotlin/cc/unitmesh/devti/gui/chat/ChatCodingComponent.kt
Expand Up @@ -2,6 +2,7 @@ package cc.unitmesh.devti.gui.chat

import cc.unitmesh.devti.AutoDevBundle
import cc.unitmesh.devti.gui.block.whenDisposed
import cc.unitmesh.devti.provider.ContextPrompter
import com.intellij.openapi.Disposable
import com.intellij.openapi.ui.NullableComponent
import com.intellij.openapi.wm.IdeFocusManager
Expand Down Expand Up @@ -64,6 +65,19 @@ class ChatCodingComponent(private val chatCodingService: ChatCodingService, val
myScrollPane.verticalScrollBar.autoscrolls = true

inputSection = AutoDevInputSection(chatCodingService, chatCodingService.project, disposable, this)
inputSection.addListener(object : AutoDevInputListener {
override fun onSubmit(component: AutoDevInputSection, trigger: AutoDevInputTrigger) {
val prompt = component.text
component.text = ""
val context = ChatContext(null, "", "")

chatCodingService.actionType = ChatActionType.CHAT
chatCodingService.handlePromptAndResponse(this@ChatCodingComponent, object : ContextPrompter() {
override fun displayPrompt() = prompt
override fun requestPrompt() = prompt
}, context)
}
})
mainPanel.add(inputSection, BorderLayout.SOUTH)

disposable?.whenDisposed(disposable) {
Expand Down
10 changes: 10 additions & 0 deletions src/main/resources/icons/send.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 4874767

Please sign in to comment.