From 4b842048a85630b82167bf0b9b6683c3acd3fa91 Mon Sep 17 00:00:00 2001 From: "bobo.yang" Date: Thu, 4 May 2023 13:50:06 +0800 Subject: [PATCH] add context by menu --- assets/chatPanel.css | 34 ++++++++++++++++ assets/chatPanel.html | 4 +- assets/inputContainer.js | 88 ++++++++++++++++++++++++++++++++++++++++ src/contextManager.ts | 41 +++++++++++++++++++ src/exampleContext.ts | 9 ++++ src/loadContexts.ts | 7 ++++ src/messageHandler.ts | 10 +++++ 7 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 src/contextManager.ts create mode 100644 src/exampleContext.ts create mode 100644 src/loadContexts.ts diff --git a/assets/chatPanel.css b/assets/chatPanel.css index c9c4ea1..dbf92fd 100644 --- a/assets/chatPanel.css +++ b/assets/chatPanel.css @@ -179,3 +179,37 @@ pre { #send-button:hover { background-color: var(--vscode-button-hover-bg); } + +.popup-context-menu { + display: none; + position: absolute; + background-color: #f9f9f9; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + padding: 12px 16px; + z-index: 1; +} +.popup-context-menu a { + color: black; + text-decoration: none; + display: block; +} +.popup-context-menu a:hover { + background-color: #f1f1f1; +} + +.popup-command-menu { + display: none; + position: absolute; + background-color: #f9f9f9; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + padding: 12px 16px; + z-index: 1; +} +.popup-command-menu a { + color: black; + text-decoration: none; + display: block; +} +.popup-command-menu a:hover { + background-color: #f1f1f1; +} \ No newline at end of file diff --git a/assets/chatPanel.html b/assets/chatPanel.html index c76d96b..b9deb14 100644 --- a/assets/chatPanel.html +++ b/assets/chatPanel.html @@ -24,7 +24,9 @@
- + + +
diff --git a/assets/inputContainer.js b/assets/inputContainer.js index 0f381d7..1f56517 100644 --- a/assets/inputContainer.js +++ b/assets/inputContainer.js @@ -19,6 +19,13 @@ autoResizeTextarea(); function initInputContainer() { const messageInput = document.getElementById('message-input'); const sendButton = document.getElementById('send-button'); + const addContextButton = document.getElementById('add-context-button'); + const addCommandButton = document.getElementById('add-command-button'); + const popupContextMenu = document.getElementById('popupContextMenu'); + const popupCommandMenu = document.getElementById('popupCommandMenu'); + + let contextList = []; + let commandList = []; messageInput.addEventListener('keypress', function (e) { if (e.key === 'Enter') { @@ -54,6 +61,20 @@ function initInputContainer() { } }); + addContextButton.addEventListener('click', (event) => { + popupContextMenu.style.display = popupContextMenu.style.display === 'block' ? 'none' : 'block'; + // 设置弹出菜单的位置 + popupContextMenu.style.left = event.pageX + 'px'; + popupContextMenu.style.top = event.pageY + 'px'; + }); + + addCommandButton.addEventListener('click', (event) => { + popupCommandMenu.style.display = popupCommandMenu.style.display === 'block' ? 'none' : 'block'; + // 设置弹出菜单的位置 + popupCommandMenu.style.left = event.pageX + 'px'; + popupCommandMenu.style.top = event.pageY + 'px'; + }); + messageUtil.registerHandler('file_select', (message) => { addFileToMessageInput(message.filePath); }); @@ -61,6 +82,73 @@ function initInputContainer() { messageUtil.registerHandler('code_select', (message) => { addCodeToMessageInput(message.codeBlock); }); + + messageUtil.registerHandler('appendContext', (message) => { + addCodeToMessageInput(message.context); + }); + + messageUtil.registerHandler('regContextList', (message) => { + contextList = message.result; + + const menuItems = []; + for (let i = 0; i < contextList.length; i++) { + menuItems.push({ + text: contextList[i].name, + href: contextList[i].name + }); + } + + menuItems.forEach(item => { + const menuItem = document.createElement('a'); + menuItem.textContent = 'add ' + item.text; + menuItem.href = item.text; + + popupContextMenu.appendChild(menuItem); + + // 为每个菜单项添加点击事件监听器 + menuItem.addEventListener('click', (event) => { + // 阻止标签的默认行为(例如导航到链接) + event.preventDefault(); + // 在此处定义点击处理逻辑 + messageUtil.sendMessage({command: 'addContext', selected: item.href}) + // 隐藏弹出菜单 + popupContextMenu.style.display = 'none'; + }); + }); + }); + + messageUtil.registerHandler('regCommandList', (message) => { + commandList = message.result; + + const menuItems = []; + for (let i = 0; i < commandList.length; i++) { + menuItems.push({ + text: commandList[i].pattern, + href: commandList[i].pattern + }); + } + + menuItems.forEach(item => { + const menuItem = document.createElement('a'); + menuItem.textContent = item.text; + menuItem.href = item.href; + + popupCommandMenu.appendChild(menuItem); + + // 为每个菜单项添加点击事件监听器 + menuItem.addEventListener('click', (event) => { + // 阻止标签的默认行为(例如导航到链接) + event.preventDefault(); + // 在此处定义点击处理逻辑 + addCodeToMessageInput("/" + item.href); + // 隐藏弹出菜单 + popupCommandMenu.style.display = 'none'; + }); + }); + }); + + messageUtil.sendMessage({command: 'regContextList'}); + messageUtil.sendMessage({command: 'regCommandList'}); } function addFileToMessageInput(filePath) { diff --git a/src/contextManager.ts b/src/contextManager.ts new file mode 100644 index 0000000..2421396 --- /dev/null +++ b/src/contextManager.ts @@ -0,0 +1,41 @@ +export interface ChatContext { + name: string; + description: string; + handler: () => Promise; + } + + class ChatContextManager { + private static instance: ChatContextManager; + private contexts: ChatContext[] = []; + + private constructor() {} + + public static getInstance(): ChatContextManager { + if (!ChatContextManager.instance) { + ChatContextManager.instance = new ChatContextManager(); + } + + return ChatContextManager.instance; + } + + registerContext(context: ChatContext): void { + this.contexts.push(context); + } + + getContextList(): ChatContext[] { + return this.contexts; + } + + async processText(command: string): Promise { + // 处理所有命令 + for (const contextObj of this.contexts) { + if (contextObj.name == command) { + return await contextObj.handler(); + } + } + + return ''; + } + } + + export default ChatContextManager; diff --git a/src/exampleContext.ts b/src/exampleContext.ts new file mode 100644 index 0000000..5c36284 --- /dev/null +++ b/src/exampleContext.ts @@ -0,0 +1,9 @@ +import { ChatContext } from './contextManager'; + +export const exampleContext: ChatContext = { + name: 'exampleContext', + description: '这是一个示例上下文', + handler: async () => { + return `[context|example file name]`; + }, +}; diff --git a/src/loadContexts.ts b/src/loadContexts.ts new file mode 100644 index 0000000..4f91cbd --- /dev/null +++ b/src/loadContexts.ts @@ -0,0 +1,7 @@ +import ChatContextManager from './contextManager'; +import { exampleContext } from './exampleContext'; + +const chatContextManager = ChatContextManager.getInstance(); + +// 注册命令 +chatContextManager.registerContext(exampleContext); \ No newline at end of file diff --git a/src/messageHandler.ts b/src/messageHandler.ts index 6b0a371..b30e822 100644 --- a/src/messageHandler.ts +++ b/src/messageHandler.ts @@ -9,7 +9,9 @@ import DtmWrapper from './dtm'; import applyCode, {applyCodeFile} from './applyCode'; import './loadCommands'; +import './loadContexts' import CommandManager, { Command } from './commandManager'; +import ChatContextManager from './contextManager'; import * as vscode3 from 'vscode'; @@ -178,6 +180,14 @@ async function handleMessage( vscode.window.showErrorMessage(`Error commit fail: ${commitResult.message} ${commitResult.log}`); } return; + case 'regContextList': + const contextList = ChatContextManager.getInstance().getContextList(); + panel.webview.postMessage({ command: 'regContextList', result: contextList }); + return; + case 'addContext': + const contextStr = await ChatContextManager.getInstance().processText(message.selected); + panel.webview.postMessage({ command: 'appendContext', context: contextStr }); + return; } }