diff --git a/CHANGELOG.md b/CHANGELOG.md index 5340928..f5acaa9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,19 @@ ## TODO * 新增MCP server功能 -* AI问答集成终端、脚本一键执行等便捷功能 * serv00 SFTP修复 * 连接服务器下拉菜单分组展示 -* 新增终端链接跳转需按住ctrl键(防止误触) * 新增服务器定时任务功能 -## [3.0.6](https://github.com/chaos-zhu/easynode/releases) (2025-04-04) +## [3.0.6](https://github.com/chaos-zhu/easynode/releases) (2025-04-xx) * AI问答支持历史记录&标题生成 +* AI对话生成脚本支持一键发送到终端(TODO +* 新增终端选中多功能菜单(一键询问AI、docker容器ID识别快捷操作. 欢迎issue反馈更多快捷功能)(TODO * 添加一些新的内置脚本 +* 新增终端链接跳转需按住ctrl|alt键(防止误触) * 修复跳板机提示多余空格 +* 修复xx情景下多终端tab同步会话bug(TODO ## [3.0.5](https://github.com/chaos-zhu/easynode/releases) (2025-04-04) diff --git a/web/package.json b/web/package.json index 544790e..c4642e6 100644 --- a/web/package.json +++ b/web/package.json @@ -25,6 +25,7 @@ "@codemirror/language": "^6.11.0", "@codemirror/legacy-modes": "^6.5.0", "@codemirror/theme-one-dark": "^6.1.2", + "@imengyu/vue3-context-menu": "^1.4.8", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-search": "^0.15.0", "@xterm/addon-web-links": "^0.11.0", diff --git a/web/src/components/ai-chat/index.vue b/web/src/components/ai-chat/index.vue index 207cb43..65947f5 100644 --- a/web/src/components/ai-chat/index.vue +++ b/web/src/components/ai-chat/index.vue @@ -182,6 +182,7 @@ import { Avatar, Refresh, CopyDocument, Delete, EditPen, CircleCheck, Loading, S import { useAIChat } from '@/composables/useAIChat' import { loadMarkdownCSS } from '@/utils/markdown' import AiApiConfig from './ai-api-config.vue' +import { EventBus } from '@/utils' const md = new MarkdownIt({ html: false, @@ -234,7 +235,7 @@ const { const aiApiConfigVisible = ref(false) const activeModel = ref(localStorage.getItem('activeModel') || '') -const question = ref('') // debian设置swap为2gb的步骤 +const question = ref('') const isDark = computed(() => $store.isDark) const isPlusActive = computed(() => $store.isPlusActive) const aiConfig = computed(() => $store.aiConfig) @@ -282,8 +283,8 @@ watch(() => visible.value, (newValue) => { const inputFocus = () => { let input = document.querySelector('.ant-sender-input') setTimeout(() => { - input.focus() - }, 100) + input?.focus() + }, 300) } const handleModelChange = (model) => { @@ -317,6 +318,12 @@ const editMessage = (content) => { question.value = content } +EventBus.$on('sendToAIInput', (text) => { + question.value = text + visible.value = true + inputFocus() +}) + const submit = async function (questionStr) { if (!questionStr || loading.value) return question.value = '' diff --git a/web/src/composables/useContextMenu.js b/web/src/composables/useContextMenu.js new file mode 100644 index 0000000..50bd777 --- /dev/null +++ b/web/src/composables/useContextMenu.js @@ -0,0 +1,34 @@ +import { ref } from 'vue' +import ContextMenu from '@imengyu/vue3-context-menu' + +export function useContextMenu() { + const menuRef = ref(null) + const showMenu = (event, items) => { + event.preventDefault() + menuRef.value = ContextMenu.showContextMenu({ + x: event.x, + y: event.y, + items: items || [ + { + label: 'A menu item', + onClick: () => { + alert('You click a menu item') + } + }, + { + label: 'A submenu' + }, + ] + }) + } + + const closeMenu = () => { + menuRef.value?.closeMenu() + } + + return { + menuRef, + showMenu, + closeMenu + } +} diff --git a/web/src/main.js b/web/src/main.js index 4fe5dfa..88dd945 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -1,6 +1,7 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' import useStore from '@store/index' +import ContextMenu from '@imengyu/vue3-context-menu' import router from './router' import tools from './plugins/tools' import elementPlugins from './plugins/element' @@ -8,6 +9,7 @@ import globalComponents from './plugins/components' import axios from '@/utils/axios' import api from './api' import App from './app.vue' +import '@imengyu/vue3-context-menu/lib/vue3-context-menu.css' import './assets/scss/reset.scss' import './assets/scss/global.scss' import './assets/scss/mobile.scss' @@ -17,6 +19,7 @@ elementPlugins(app) globalComponents(app) app.use(createPinia()) app.use(router) +app.use(ContextMenu) app.config.globalProperties.$api = api app.config.globalProperties.$tools = tools diff --git a/web/src/store/index.js b/web/src/store/index.js index 27eaf1b..762cf6a 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -38,8 +38,6 @@ const useStore = defineStore('global', { fontSize: 16, themeName: 'Afterglow', background: 'linear-gradient(-225deg, #CBBACC 0%, #2580B3 100%)', - quickCopy: isHttps(), - quickPaste: isHttps(), autoReconnect: true, autoExecuteScript: false }, diff --git a/web/src/views/terminal/components/terminal-setting.vue b/web/src/views/terminal/components/terminal-setting.vue index 25e7e0e..1e1facb 100644 --- a/web/src/views/terminal/components/terminal-setting.vue +++ b/web/src/views/terminal/components/terminal-setting.vue @@ -82,38 +82,6 @@ - - - - - - - - - - - + 分组级联展示 不分组单列展示 @@ -200,18 +173,10 @@ const fontSize = computed({ get: () => $store.terminalConfig.fontSize, set: (newVal) => $store.setTerminalSetting({ fontSize: newVal }) }) -const quickCopy = computed({ - get: () => $store.terminalConfig.quickCopy, - set: (newVal) => $store.setTerminalSetting({ quickCopy: newVal }) -}) const autoReconnect = computed({ get: () => $store.terminalConfig.autoReconnect, set: (newVal) => $store.setTerminalSetting({ autoReconnect: newVal }) }) -const quickPaste = computed({ - get: () => $store.terminalConfig.quickPaste, - set: (newVal) => $store.setTerminalSetting({ quickPaste: newVal }) -}) const autoExecuteScript = computed({ get: () => $store.terminalConfig.autoExecuteScript, set: (newVal) => $store.setTerminalSetting({ autoExecuteScript: newVal }) diff --git a/web/src/views/terminal/components/terminal-tab.vue b/web/src/views/terminal/components/terminal-tab.vue index bbd42ff..e91ff7c 100644 --- a/web/src/views/terminal/components/terminal-tab.vue +++ b/web/src/views/terminal/components/terminal-tab.vue @@ -4,6 +4,7 @@ ref="terminalRef" class="terminal_container" @contextmenu="handleRightClick" + @mouseup="handleMouseUp" />