完成标签页的保存
This commit is contained in:
parent
3d372dafae
commit
b156244763
@ -16,6 +16,7 @@ import { pinkLog } from './views/setting/util';
|
||||
import { acquireVsCodeApi, useMessageBridge } from './api/message-bridge';
|
||||
import { connectionArgs, connectionMethods, connectionResult, doConnect } from './views/connect/connection';
|
||||
import { loadSetting } from './hook/setting';
|
||||
import { loadPanels } from './hook/panel';
|
||||
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
@ -47,11 +48,19 @@ onMounted(() => {
|
||||
connectionResult.logString = msg;
|
||||
}, { once: true });
|
||||
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
// 初始化 设置
|
||||
loadSetting();
|
||||
|
||||
// 初始化 tab
|
||||
loadPanels();
|
||||
|
||||
doConnect();
|
||||
|
||||
// 200 是我的电脑上的 ws 的连接时间,部署环境中不存在 ws 连接延时的问题,所以
|
||||
// 可以直接不管
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
<span
|
||||
class="add-button iconfont icon-add"
|
||||
@click="addNewTab"
|
||||
@click="pageAddNewTab"
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
@ -38,9 +38,23 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { addNewTab, tabs, setActiveTab, closeTab } from './panel';
|
||||
|
||||
defineComponent({ name: 'main-panel' });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
function pageAddNewTab() {
|
||||
addNewTab();
|
||||
|
||||
// 如果当前不在 debug 路由,则切换到 debug 路由
|
||||
if (route.name !== 'debug') {
|
||||
router.push('/debug');
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { reactive } from 'vue';
|
||||
import { watch, reactive } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import Resource from './resource/index.vue';
|
||||
import Chat from './chat/index.vue';
|
||||
import Prompt from './prompt/index.vue';
|
||||
import Tool from './tool/index.vue';
|
||||
import I18n from '@/i18n/index';
|
||||
import { safeSavePanels, savePanels } from '@/hook/panel';
|
||||
|
||||
const { t } = I18n.global;
|
||||
|
||||
@ -13,6 +15,7 @@ interface Tab {
|
||||
icon: string;
|
||||
type: string;
|
||||
component: any;
|
||||
componentIndex: number;
|
||||
storage: Record<string, any>;
|
||||
}
|
||||
|
||||
@ -37,6 +40,17 @@ export const tabs = reactive<{
|
||||
|
||||
let tabCounter = 1;
|
||||
|
||||
// 监控 tabs
|
||||
|
||||
watch(
|
||||
() => tabs,
|
||||
(newValue, oldValue) => {
|
||||
console.log('state change');
|
||||
safeSavePanels();
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
function createTab(type: string, index: number): Tab {
|
||||
let customName: string | null = null;
|
||||
|
||||
@ -52,6 +66,7 @@ function createTab(type: string, index: number): Tab {
|
||||
},
|
||||
icon: 'icon-blank',
|
||||
type,
|
||||
componentIndex: -1,
|
||||
component: undefined,
|
||||
storage: {},
|
||||
};
|
||||
|
94
app/src/hook/panel.ts
Normal file
94
app/src/hook/panel.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { useMessageBridge } from "@/api/message-bridge";
|
||||
import { llmManager, llms } from "@/views/setting/llm";
|
||||
import { pinkLog } from "@/views/setting/util";
|
||||
import I18n from '@/i18n/index';
|
||||
import { debugModes, tabs } from "@/components/main-panel/panel";
|
||||
import { markRaw } from "vue";
|
||||
|
||||
interface SaveTabItem {
|
||||
name: string;
|
||||
icon: string;
|
||||
type: string;
|
||||
componentIndex: number;
|
||||
storage: Record<string, any>;
|
||||
}
|
||||
|
||||
interface SaveTab {
|
||||
tabs: SaveTabItem[]
|
||||
currentIndex: number
|
||||
}
|
||||
|
||||
export function loadPanels() {
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
bridge.addCommandListener('panel/load', data => {
|
||||
const persistTab = data.msg as SaveTab;
|
||||
|
||||
if (persistTab.tabs.length === 0) {
|
||||
// 空的,直接返回不需要管
|
||||
return;
|
||||
}
|
||||
|
||||
tabs.activeIndex = 0;
|
||||
tabs.content = [];
|
||||
|
||||
for (const tab of persistTab.tabs || []) {
|
||||
tabs.content.push({
|
||||
name: tab.name,
|
||||
icon: tab.icon,
|
||||
type: tab.type,
|
||||
componentIndex: tab.componentIndex,
|
||||
component: markRaw(debugModes[tab.componentIndex]),
|
||||
storage: tab.storage
|
||||
});
|
||||
}
|
||||
|
||||
tabs.activeIndex = persistTab.currentIndex;
|
||||
|
||||
}, { once: true });
|
||||
|
||||
bridge.postMessage({
|
||||
command: 'panel/load'
|
||||
});
|
||||
}
|
||||
|
||||
let debounceHandler: number;
|
||||
|
||||
export function safeSavePanels() {
|
||||
clearTimeout(debounceHandler);
|
||||
debounceHandler = setTimeout(() => {
|
||||
savePanels();
|
||||
}, 200);
|
||||
}
|
||||
|
||||
export function savePanels(saveHandler?: () => void) {
|
||||
const bridge = useMessageBridge();
|
||||
|
||||
const saveTabs: SaveTab = {
|
||||
currentIndex: tabs.activeIndex,
|
||||
tabs: []
|
||||
};
|
||||
for (const tab of tabs.content) {
|
||||
saveTabs.tabs.push({
|
||||
name: tab.name,
|
||||
icon: tab.icon,
|
||||
type: tab.type,
|
||||
componentIndex: tab.componentIndex,
|
||||
storage: JSON.parse(JSON.stringify(tab.storage))
|
||||
});
|
||||
}
|
||||
|
||||
bridge.addCommandListener('panel/save', data => {
|
||||
const saveStatusCode = data.code;
|
||||
pinkLog('配置保存状态:' + saveStatusCode);
|
||||
|
||||
if (saveHandler) {
|
||||
saveHandler();
|
||||
}
|
||||
}, { once: true });
|
||||
|
||||
bridge.postMessage({
|
||||
command: 'panel/save',
|
||||
data: saveTabs
|
||||
});
|
||||
}
|
@ -59,6 +59,7 @@ function chooseDebugMode(index: number) {
|
||||
if (connectionResult.success) {
|
||||
const activeTab = tabs.activeTab;
|
||||
activeTab.component = markRaw(debugModes[index]);
|
||||
activeTab.componentIndex = index;
|
||||
activeTab.icon = debugOptions[index].icon;
|
||||
|
||||
// 此处可以这么做是因为这个操作过后 activeTab 绑定的 tab 的 name 就不会再被进行赋值操作了
|
||||
|
@ -2,6 +2,7 @@
|
||||
import { VSCodeWebViewLike } from '../adapter';
|
||||
import { connect, MCPClient, type MCPOptions } from './connect';
|
||||
import { callTool, getPrompt, listPrompts, listResources, listResourceTemplates, listTools, readResource } from './handler';
|
||||
import { panelLoadHandler, panelSaveHandler } from './panel';
|
||||
import { settingLoadHandler, settingSaveHandler } from './setting';
|
||||
import { ping } from './util';
|
||||
|
||||
@ -78,6 +79,14 @@ export function messageController(command: string, data: any, webview: VSCodeWeb
|
||||
settingLoadHandler(client, webview);
|
||||
break;
|
||||
|
||||
case 'panel/save':
|
||||
panelSaveHandler(client, data, webview);
|
||||
break;
|
||||
|
||||
case 'panel/load':
|
||||
panelLoadHandler(client, webview);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
69
test/src/controller/panel.ts
Normal file
69
test/src/controller/panel.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { VSCodeWebViewLike } from '../adapter';
|
||||
import { loadConfig, loadTabSaveConfig, saveConfig, saveTabSaveConfig } from '../util';
|
||||
import { MCPClient } from './connect';
|
||||
|
||||
export async function panelSaveHandler(client: MCPClient | undefined, data: any, webview: VSCodeWebViewLike) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
msg: 'mcp client 尚未连接'
|
||||
};
|
||||
webview.postMessage({ command: 'ping', data: connectResult });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// 保存配置
|
||||
saveTabSaveConfig(data);
|
||||
|
||||
webview.postMessage({
|
||||
command: 'panel/save',
|
||||
data: {
|
||||
code: 200,
|
||||
msg: 'Settings saved successfully'
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
webview.postMessage({
|
||||
command: 'panel/save',
|
||||
data: {
|
||||
code: 500,
|
||||
msg: `Failed to save settings: ${(error as Error).message}`
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function panelLoadHandler(client: MCPClient | undefined, webview: VSCodeWebViewLike) {
|
||||
if (!client) {
|
||||
const connectResult = {
|
||||
code: 501,
|
||||
msg: 'mcp client 尚未连接'
|
||||
};
|
||||
webview.postMessage({ command: 'ping', data: connectResult });
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// 加载配置
|
||||
const config = loadTabSaveConfig();
|
||||
|
||||
webview.postMessage({
|
||||
command: 'panel/load',
|
||||
data: {
|
||||
code: 200,
|
||||
msg: config // 直接返回配置对象
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
webview.postMessage({
|
||||
command: 'panel/load',
|
||||
data: {
|
||||
code: 500,
|
||||
msg: `Failed to load settings: ${(error as Error).message}`
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -17,6 +17,21 @@ function getConfigurationPath() {
|
||||
return 'config.json';
|
||||
}
|
||||
|
||||
function getTabSavePath() {
|
||||
// 如果是 vscode 插件下,则修改为 ~/.vscode/openmcp.json
|
||||
if (process.env.VSCODE_PID) {
|
||||
// 在 VSCode 插件环境下
|
||||
// const homeDir = os.homedir();
|
||||
// const configDir = path.join(homeDir, '.openmcp');
|
||||
// if (!fs.existsSync(configDir)) {
|
||||
// fs.mkdirSync(configDir, { recursive: true });
|
||||
// }
|
||||
// return path.join(configDir, 'tabs.json');
|
||||
return 'tabs.json';
|
||||
}
|
||||
return 'tabs.json';
|
||||
}
|
||||
|
||||
function getDefaultLanguage() {
|
||||
if (process.env.VSCODE_PID) {
|
||||
// TODO: 获取 vscode 内部的语言
|
||||
@ -36,6 +51,24 @@ const DEFAULT_CONFIG: IConfig = {
|
||||
LANG: getDefaultLanguage()
|
||||
};
|
||||
|
||||
interface SaveTabItem {
|
||||
name: string;
|
||||
icon: string;
|
||||
type: string;
|
||||
componentIndex: number;
|
||||
storage: Record<string, any>;
|
||||
}
|
||||
|
||||
interface SaveTab {
|
||||
tabs: SaveTabItem[]
|
||||
currentIndex: number
|
||||
}
|
||||
|
||||
const DEFAULT_TABS: SaveTab = {
|
||||
tabs: [],
|
||||
currentIndex: -1
|
||||
}
|
||||
|
||||
function createConfig(): IConfig {
|
||||
const configPath = getConfigurationPath();
|
||||
const configDir = path.dirname(configPath);
|
||||
@ -50,6 +83,20 @@ function createConfig(): IConfig {
|
||||
return DEFAULT_CONFIG;
|
||||
}
|
||||
|
||||
function createSaveTabConfig(): SaveTab {
|
||||
const configPath = getTabSavePath();
|
||||
const configDir = path.dirname(configPath);
|
||||
|
||||
// 确保配置目录存在
|
||||
if (configDir && !fs.existsSync(configDir)) {
|
||||
fs.mkdirSync(configDir, { recursive: true });
|
||||
}
|
||||
|
||||
// 写入默认配置
|
||||
fs.writeFileSync(configPath, JSON.stringify(DEFAULT_TABS, null, 2), 'utf-8');
|
||||
return DEFAULT_TABS;
|
||||
}
|
||||
|
||||
export function loadConfig(): IConfig {
|
||||
const configPath = getConfigurationPath();
|
||||
|
||||
@ -66,22 +113,39 @@ export function loadConfig(): IConfig {
|
||||
}
|
||||
}
|
||||
|
||||
export function saveConfig(config: Partial<IConfig>, merge: boolean = true): void {
|
||||
export function saveConfig(config: Partial<IConfig>): void {
|
||||
const configPath = getConfigurationPath();
|
||||
let currentConfig: IConfig = DEFAULT_CONFIG;
|
||||
|
||||
if (merge && fs.existsSync(configPath)) {
|
||||
try {
|
||||
currentConfig = loadConfig();
|
||||
} catch (error) {
|
||||
console.error('Error loading existing config:', error);
|
||||
}
|
||||
}
|
||||
|
||||
const newConfig = { ...currentConfig, ...config };
|
||||
|
||||
try {
|
||||
fs.writeFileSync(configPath, JSON.stringify(newConfig, null, 2), 'utf-8');
|
||||
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
||||
} catch (error) {
|
||||
console.error('Error saving config file:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function loadTabSaveConfig(): SaveTab {
|
||||
const tabSavePath = getTabSavePath();
|
||||
|
||||
if (!fs.existsSync(tabSavePath)) {
|
||||
return createSaveTabConfig();
|
||||
}
|
||||
|
||||
try {
|
||||
const configData = fs.readFileSync(tabSavePath, 'utf-8');
|
||||
return JSON.parse(configData) as SaveTab;
|
||||
} catch (error) {
|
||||
console.error('Error loading config file, creating new one:', error);
|
||||
return createSaveTabConfig();
|
||||
}
|
||||
}
|
||||
|
||||
export function saveTabSaveConfig(config: Partial<IConfig>): void {
|
||||
const tabSavePath = getTabSavePath();
|
||||
|
||||
try {
|
||||
fs.writeFileSync(tabSavePath, JSON.stringify(config, null, 2), 'utf-8');
|
||||
} catch (error) {
|
||||
console.error('Error saving config file:', error);
|
||||
throw error;
|
||||
|
23
test/tabs.json
Normal file
23
test/tabs.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"currentIndex": 1,
|
||||
"tabs": [
|
||||
{
|
||||
"name": "资源",
|
||||
"icon": "icon-file",
|
||||
"type": "blank",
|
||||
"componentIndex": 0,
|
||||
"storage": {
|
||||
"currentResourceName": "greeting"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "提词",
|
||||
"icon": "icon-chat",
|
||||
"type": "blank",
|
||||
"componentIndex": 1,
|
||||
"storage": {
|
||||
"currentPromptName": "translate"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user