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"
/>