diff --git a/src/util/ideaBridge.ts b/src/util/ideaBridge.ts index 7af49be..b80a854 100644 --- a/src/util/ideaBridge.ts +++ b/src/util/ideaBridge.ts @@ -240,6 +240,19 @@ const JStoIdea = { }, }; + window.JSJavaBridge.callJava(JSON.stringify(params)); + }, + userInput: (message) => { + const params = { + action: "input/request", + metadata: { + callback: "IdeaToJSMessage", + }, + payload: { + data: message?.text || "", + }, + }; + window.JSJavaBridge.callJava(JSON.stringify(params)); }, }; @@ -437,6 +450,7 @@ class IdeaBridge { } sendMessage(message: any) { + console.log("sendMessage message: ", message); // 根据 command 分发到不同的方法· switch (message.command) { // 发送消息 @@ -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(