From e1ff05e3e52530009c6b277245eb71d7b6ed8483 Mon Sep 17 00:00:00 2001 From: smallstone <> Date: Mon, 25 Dec 2023 19:09:59 +0800 Subject: [PATCH 1/5] Fix user input handling in ChatStore and ideaBridge This commit fixes the user input handling in the ChatStore and ideaBridge files. It ensures that the user input is correctly passed to the JSJavaBridge and that the appropriate action is taken. --- src/util/ideaBridge.ts | 17 + src/views/components/ChatMark/index.tsx | 587 +++++++++++++----------- src/views/stores/ChatStore.ts | 2 +- 3 files changed, 332 insertions(+), 274 deletions(-) diff --git a/src/util/ideaBridge.ts b/src/util/ideaBridge.ts index 7af49be..513ef77 100644 --- a/src/util/ideaBridge.ts +++ b/src/util/ideaBridge.ts @@ -240,6 +240,20 @@ const JStoIdea = { }, }; + window.JSJavaBridge.callJava(JSON.stringify(params)); + }, + userInput: (message) => { + const params = { + action: "userInput/request", + metadata: { + callback: "IdeaToJSMessage", + }, + payload: { + data: message?.text || "", + }, + }; + console.log("userInput params: ", params); + window.JSJavaBridge.callJava(JSON.stringify(params)); }, }; @@ -498,6 +512,9 @@ class IdeaBridge { case "openLink": JStoIdea.openLink(message); break; + case "userInput": + JStoIdea.userInput(message); + break; default: break; } diff --git a/src/views/components/ChatMark/index.tsx b/src/views/components/ChatMark/index.tsx index 688e12b..142cb55 100644 --- a/src/views/components/ChatMark/index.tsx +++ b/src/views/components/ChatMark/index.tsx @@ -1,289 +1,330 @@ -import React, { useEffect, useState } from 'react'; -import { Box, Button, Checkbox, Text, Radio, Textarea, createStyles } from '@mantine/core'; -import { useListState, useSetState } from '@mantine/hooks'; -import { useMst } from '@/views/stores/RootStore'; -import yaml from 'js-yaml'; +import React, { useEffect, useState } from "react"; +import { + Box, + Button, + Checkbox, + Text, + Radio, + Textarea, + createStyles, +} from "@mantine/core"; +import { useListState, useSetState } from "@mantine/hooks"; +import { useMst } from "@/views/stores/RootStore"; +import yaml from "js-yaml"; const useStyles = createStyles((theme) => ({ - container:{ - padding:0, - margin:0, - }, - submit:{ - marginTop:theme.spacing.xs, - marginRight:theme.spacing.xs, - marginBottom:theme.spacing.xs, - }, - cancel:{ - }, - button:{ - marginTop:theme.spacing.xs, - marginRight:theme.spacing.xs, - marginBottom:theme.spacing.xs, - }, - checkbox:{ - marginTop:theme.spacing.xs, - marginBottom:theme.spacing.xs, - }, - label:{ - color:'var(--vscode-editor-foreground)', - }, - radio:{ - marginTop:theme.spacing.xs, - marginBottom:theme.spacing.xs, - }, - editor:{ - backgroundColor: 'var(--vscode-input-background)', - borderColor: 'var(--vscode-input-border)', - color: 'var(--vscode-input-foreground)', - }, - editorWrapper:{ - marginTop:theme.spacing.xs, - marginBottom:theme.spacing.xs, - } - })); + container: { + padding: 0, + margin: 0, + }, + submit: { + marginTop: theme.spacing.xs, + marginRight: theme.spacing.xs, + marginBottom: theme.spacing.xs, + }, + cancel: {}, + button: { + marginTop: theme.spacing.xs, + marginRight: theme.spacing.xs, + marginBottom: theme.spacing.xs, + }, + checkbox: { + marginTop: theme.spacing.xs, + marginBottom: theme.spacing.xs, + }, + label: { + color: "var(--vscode-editor-foreground)", + }, + radio: { + marginTop: theme.spacing.xs, + marginBottom: theme.spacing.xs, + }, + editor: { + backgroundColor: "var(--vscode-input-background)", + borderColor: "var(--vscode-input-border)", + color: "var(--vscode-input-foreground)", + }, + editorWrapper: { + marginTop: theme.spacing.xs, + marginBottom: theme.spacing.xs, + }, +})); -interface Wdiget{ - id:string, - value:string, - title?:string, - type:'editor'|'checkbox'|'radio'|'button'|'text' +interface Wdiget { + id: string; + value: string; + title?: string; + type: "editor" | "checkbox" | "radio" | "button" | "text"; } - -const ChatMark = ({ children,value,messageDone }) => { - const {classes} = useStyles(); - const [widgets,widgetsHandlers] = useListState(); - const {chat} = useMst(); - const [autoForm,setAutoForm] = useState(false); // if any widget is checkbox,radio or editor wdiget, the form is auto around them - const values = value?yaml.load(value):{}; - const [disabled,setDisabled] = useState(messageDone||!!value); - const handleSubmit = () => { - let formData = {}; - widgets.forEach((widget)=>{ - if(widget.type === 'text' - || widget.type === 'button' - || (widget.type === 'radio' && widget.value === 'unchecked') - || (widget.type === 'checkbox' && widget.value === 'unchecked')){ - // ignore - return; - } - formData[widget.id] = widget.value; - }); - chat.userInput(formData); - }; +const ChatMark = ({ children, value, messageDone }) => { + const { classes } = useStyles(); + const [widgets, widgetsHandlers] = useListState(); + const { chat } = useMst(); + const [autoForm, setAutoForm] = useState(false); // if any widget is checkbox,radio or editor wdiget, the form is auto around them + const values = value ? yaml.load(value) : {}; + const [disabled, setDisabled] = useState(messageDone || !!value); - const handleCancel = () => { - chat.userInput({ - 'form':'canceled' - }); - }; + const handleSubmit = () => { + let formData = {}; + widgets.forEach((widget) => { + if ( + widget.type === "text" || + widget.type === "button" || + (widget.type === "radio" && widget.value === "unchecked") || + (widget.type === "checkbox" && widget.value === "unchecked") + ) { + // ignore + return; + } + formData[widget.id] = widget.value; + }); + chat.userInput(formData); + }; - const handleButtonClick = ({event,index}) => { - const widget = widgets[index]; - widget['value'] = event.currentTarget.value;; - widgetsHandlers.setItem(index,widget); - chat.userInput({ - [widget['id']]:'clicked' + const handleCancel = () => { + chat.userInput({ + form: "canceled", + }); + }; + + const handleButtonClick = ({ event, index }) => { + const widget = widgets[index]; + widget["value"] = event.currentTarget.value; + widgetsHandlers.setItem(index, widget); + chat.userInput({ + [widget["id"]]: "clicked", + }); + }; + + const handleCheckboxChange = ({ event, index }) => { + const widget = widgets[index]; + widget["value"] = event.currentTarget.checked ? "checked" : "unchecked"; + widgetsHandlers.setItem(index, widget); + }; + const handleRadioChange = ({ event, allValues }) => { + widgetsHandlers.apply((item, index) => { + if (allValues.includes(item.id)) { + if (item.id === event) { + item.value = "checked"; + } else { + item.value = "unchecked"; + } + } + return item; + }); + }; + const handleEditorChange = ({ event, index }) => { + const widget = widgets[index]; + widget["value"] = event.currentTarget.value; + widgetsHandlers.setItem(index, widget); + }; + + useEffect(() => { + const lines = children.split("\n"); + let detectEditorId = ""; + let editorContentRecorder = ""; + + const textRegex = /^([^>].*)/; // Text widget + const buttonRegex = /^>\s*\((.*?)\)\s*(.*)/; // Button widget + const checkboxRegex = /^>\s*\[([x ]*)\]\((.*?)\)\s*(.*)/; // Checkbox widget + const radioRegex = /^>\s*-\s*\((.*?)\)\s*(.*)/; // Radio button widget + const editorRegex = /^>\s*\|\s*\((.*?)\)/; // Editor widget + const editorContentRegex = /^>\s*(.*)/; // Editor widget + + lines.forEach((line, index) => { + let match; + + if ((match = line.match(textRegex))) { + widgetsHandlers.append({ + id: `text${index}`, + type: "text", + value: line, }); - }; - - const handleCheckboxChange = ({event,index})=>{ - const widget = widgets[index]; - widget['value'] = event.currentTarget.checked?'checked':'unchecked'; - widgetsHandlers.setItem(index,widget); - }; - const handleRadioChange = ({event,allValues})=>{ - widgetsHandlers.apply((item, index) => { - if(allValues.includes(item.id)){ - if(item.id === event){ - item.value = 'checked'; - }else{ - item.value = 'unchecked'; - } + } else if ((match = line.match(buttonRegex))) { + const [id, title] = match.slice(1); + widgetsHandlers.append({ + id, + title, + type: "button", + value: id, + }); + } else if ((match = line.match(checkboxRegex))) { + const [status, id, title] = match.slice(1); + widgetsHandlers.append({ + id, + title, + type: "checkbox", + value: value ? "unchecked" : status === "x" ? "checked" : "unchecked", + }); + setAutoForm(true); + } else if ((match = line.match(radioRegex))) { + const [id, title] = match.slice(1); + widgetsHandlers.append({ + id, + title, + type: "radio", + value: "unchecked", + }); + setAutoForm(true); + } else if ((match = line.match(editorRegex))) { + const [id] = match.slice(1); + detectEditorId = id; + widgetsHandlers.append({ + id, + type: "editor", + value: "", + }); + setAutoForm(true); + } else if ((match = line.match(editorContentRegex))) { + const [content] = match.slice(1); + editorContentRecorder += content + "\n"; + } + // if next line is not editor, then end current editor + const nextLine = index + 1 < lines.length ? lines[index + 1] : null; + if (detectEditorId && (!nextLine || !nextLine.startsWith(">"))) { + // remove last \n + editorContentRecorder = editorContentRecorder.substring( + 0, + editorContentRecorder.length - 1 + ); + // apply editor content to widget + ((editorId, editorContent) => + widgetsHandlers.apply((item) => { + if (item.id === editorId && !(item.id in values)) { + item.value = editorContent; } return item; - }); - }; - const handleEditorChange = ({event,index})=>{ - const widget = widgets[index]; - widget['value'] = event.currentTarget.value; - widgetsHandlers.setItem(index,widget); - }; - - useEffect(()=>{ - - const lines = children.split('\n'); - let detectEditorId = ''; - let editorContentRecorder = ''; - - const textRegex = /^([^>].*)/; // Text widget - const buttonRegex = /^>\s*\((.*?)\)\s*(.*)/; // Button widget - const checkboxRegex = /^>\s*\[([x ]*)\]\((.*?)\)\s*(.*)/; // Checkbox widget - const radioRegex = /^>\s*-\s*\((.*?)\)\s*(.*)/; // Radio button widget - const editorRegex = /^>\s*\|\s*\((.*?)\)/; // Editor widget - const editorContentRegex = /^>\s*(.*)/; // Editor widget - - lines.forEach((line, index) => { - - let match; - - if (match = line.match(textRegex)) { - widgetsHandlers.append({ - id:`text${index}`, - type:'text', - value:line, - }); - } else if (match = line.match(buttonRegex)) { - const [id, title] = match.slice(1); - widgetsHandlers.append({ - id, - title, - type:'button', - value: id - }); - } else if (match = line.match(checkboxRegex)) { - const [status, id, title] = match.slice(1); - widgetsHandlers.append({ - id, - title, - type:'checkbox', - value: value?'unchecked':status === 'x'?'checked':'unchecked', - }); - setAutoForm(true); - } else if (match = line.match(radioRegex)) { - const [id, title] = match.slice(1); - widgetsHandlers.append({ - id, - title, - type:'radio', - value:'unchecked', - }); - setAutoForm(true); - } else if (match = line.match(editorRegex)) { - const [id] = match.slice(1); - detectEditorId = id; - widgetsHandlers.append({ - id, - type:'editor', - value: '', - }); - setAutoForm(true); - } else if(match = line.match(editorContentRegex)){ - const [content] = match.slice(1); - editorContentRecorder += content + '\n'; - } - // if next line is not editor, then end current editor - const nextLine = index + 1 < lines.length? lines[index + 1]:null; - if (detectEditorId && (!nextLine || !nextLine.startsWith('>'))) { - // remove last \n - editorContentRecorder = editorContentRecorder.substring(0, editorContentRecorder.length - 1); - // apply editor content to widget - ((editorId,editorContent) => widgetsHandlers.apply((item)=>{ - if(item.id === editorId && !(item.id in values)){ - item.value = editorContent; - } - return item; - }))(detectEditorId,editorContentRecorder); - // reset editor - detectEditorId = ''; - editorContentRecorder = ''; - } - }); - for (const key in values) { - widgetsHandlers.apply((item)=>{ - if(item.id === key){ - item.value = values[key]; - } - return item; - }); + }))(detectEditorId, editorContentRecorder); + // reset editor + detectEditorId = ""; + editorContentRecorder = ""; + } + }); + for (const key in values) { + widgetsHandlers.apply((item) => { + if (item.id === key) { + item.value = values[key]; } - },[]); - // Render markdown widgets - const renderWidgets = (widgets) => { - let radioGroupTemp:any = []; - let radioValuesTemp:any = []; - let wdigetsTemp:any = []; - widgets.map((widget, index) => { - if (widget.type === 'text') { - wdigetsTemp.push({widget.value}); - } else if (widget.type === 'button') { - wdigetsTemp.push(); - } else if (widget.type === 'checkbox') { - wdigetsTemp.push( handleCheckboxChange({event,index})}/>); - } else if (widget.type === 'radio') { - radioValuesTemp.push(widget.id); - radioGroupTemp.push(); - // if next widget is not radio, then end current group - const nextWidget = index + 1 < widgets.length? widgets[index + 1]:null; - if (!nextWidget || nextWidget.type !== 'radio') { - const radioGroup = ((radios,allValues)=>{ - const filteredValues = allValues.filter((value) => values[value] === 'checked'); - return 0 ? filteredValues[0] : undefined} - onChange={ - event => handleRadioChange({ - event, - allValues - }) - }> - {radios} - ; - })(radioGroupTemp,radioValuesTemp); - radioGroupTemp = []; - radioValuesTemp = []; - wdigetsTemp.push(radioGroup); + return item; + }); + } + }, []); + // Render markdown widgets + const renderWidgets = (widgets) => { + let radioGroupTemp: any = []; + let radioValuesTemp: any = []; + let wdigetsTemp: any = []; + widgets.map((widget, index) => { + if (widget.type === "text") { + wdigetsTemp.push({widget.value}); + } else if (widget.type === "button") { + wdigetsTemp.push( + + ); + } else if (widget.type === "checkbox") { + wdigetsTemp.push( + handleCheckboxChange({ event, index })} + /> + ); + } else if (widget.type === "radio") { + radioValuesTemp.push(widget.id); + radioGroupTemp.push( + + ); + // if next widget is not radio, then end current group + const nextWidget = + index + 1 < widgets.length ? widgets[index + 1] : null; + if (!nextWidget || nextWidget.type !== "radio") { + const radioGroup = ((radios, allValues) => { + const filteredValues = allValues.filter( + (value) => values[value] === "checked" + ); + return ( + 0 ? filteredValues[0] : undefined } - } else if (widget.type === 'editor') { - wdigetsTemp.push(