From ecc66e12c390964f17950db43307435a703b71cc Mon Sep 17 00:00:00 2001 From: purocean Date: Fri, 1 May 2026 12:10:27 +0800 Subject: [PATCH] feat: expose user actions to mcp --- src/renderer/components/ControlCenter.vue | 2 ++ src/renderer/components/DefaultPreviewerRender.ce.vue | 4 +++- src/renderer/components/DocHistory.vue | 2 ++ src/renderer/components/Editor.vue | 2 ++ src/renderer/components/ExtensionManager.vue | 2 ++ src/renderer/components/FileTabs.vue | 10 ++++++++++ src/renderer/components/Filter.vue | 2 ++ src/renderer/components/FindInPreview.vue | 2 ++ src/renderer/components/KeyboardShortcuts.vue | 4 +++- src/renderer/components/SearchPanel.vue | 2 ++ src/renderer/components/Tree.vue | 2 ++ src/renderer/plugins/copy-content.tsx | 2 ++ src/renderer/plugins/editor-markdown.ts | 2 ++ src/renderer/plugins/electron-zoom.ts | 6 ++++++ src/renderer/plugins/history-stack.ts | 4 ++++ src/renderer/plugins/image-localization.ts | 2 ++ src/renderer/plugins/insert-table/index.ts | 2 ++ src/renderer/plugins/status-bar-premium.ts | 2 ++ src/renderer/plugins/status-bar-repository-switch.ts | 2 ++ src/renderer/plugins/status-bar-theme.ts | 2 ++ src/renderer/plugins/switch-todo.ts | 2 ++ src/renderer/plugins/view-links/index.ts | 2 ++ src/renderer/services/editor.ts | 2 ++ src/renderer/services/layout.ts | 8 ++++++++ src/renderer/services/tree.ts | 2 ++ src/renderer/services/view.ts | 2 ++ src/renderer/services/workbench.ts | 2 ++ src/renderer/startup.ts | 2 ++ 28 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/renderer/components/ControlCenter.vue b/src/renderer/components/ControlCenter.vue index 9abe5441..f52b07a1 100644 --- a/src/renderer/components/ControlCenter.vue +++ b/src/renderer/components/ControlCenter.vue @@ -45,6 +45,8 @@ registerAction({ name: 'control-center.toggle', description: t('command-desc.control-center_toggle'), forUser: true, + forMcp: true, + mcpDescription: 'Toggle control center. Args: [visible:boolean?]. No return.', handler: toggle, keys: [Alt, 'c'] }) diff --git a/src/renderer/components/DefaultPreviewerRender.ce.vue b/src/renderer/components/DefaultPreviewerRender.ce.vue index 108dc20d..14209ccd 100644 --- a/src/renderer/components/DefaultPreviewerRender.ce.vue +++ b/src/renderer/components/DefaultPreviewerRender.ce.vue @@ -263,7 +263,9 @@ onMounted(() => { description: t('command-desc.view_refresh'), handler: refresh, keys: [CtrlCmd, 'r'], - forUser: true + forUser: true, + forMcp: true, + mcpDescription: 'Refresh preview. No args. No return.' }) registerHook('SETTING_CHANGED', updateMarkdownBodyMaxWidth) diff --git a/src/renderer/components/DocHistory.vue b/src/renderer/components/DocHistory.vue index 031eb586..d7cc0154 100644 --- a/src/renderer/components/DocHistory.vue +++ b/src/renderer/components/DocHistory.vue @@ -380,6 +380,8 @@ onMounted(() => { handler: show, keys: [Alt, 'h'], forUser: true, + forMcp: true, + mcpDescription: 'Show document history panel. No args. No return.', description: t('command-desc.doc_show-history'), }) registerAction({ name: 'doc.hide-history', handler: hide }) diff --git a/src/renderer/components/Editor.vue b/src/renderer/components/Editor.vue index a554b623..7ad5fc96 100644 --- a/src/renderer/components/Editor.vue +++ b/src/renderer/components/Editor.vue @@ -104,6 +104,8 @@ onMounted(() => { name: 'editor.rotate-custom-editors', description: t('command-desc.editor_rotate-custom-editors'), forUser: true, + forMcp: true, + mcpDescription: 'Rotate through custom editors. No args. No return.', handler: () => { if (availableEditors.value.length > 1) { const index = availableEditors.value.findIndex(x => x.name === store.state.editor) diff --git a/src/renderer/components/ExtensionManager.vue b/src/renderer/components/ExtensionManager.vue index bfcdd795..2ac3c721 100644 --- a/src/renderer/components/ExtensionManager.vue +++ b/src/renderer/components/ExtensionManager.vue @@ -674,6 +674,8 @@ onMounted(() => { description: t('command-desc.extension_show-manager'), handler: show, forUser: true, + forMcp: true, + mcpDescription: 'Show extension manager. No args. No return.', }) }) diff --git a/src/renderer/components/FileTabs.vue b/src/renderer/components/FileTabs.vue index 63e7ba80..eb7f4935 100644 --- a/src/renderer/components/FileTabs.vue +++ b/src/renderer/components/FileTabs.vue @@ -234,6 +234,8 @@ export default defineComponent({ description: t('command-desc.file-tabs_switch-left'), keys: [CtrlCmd, Alt, 'ArrowLeft'], forUser: true, + forMcp: true, + mcpDescription: 'Switch to left tab. No args. No return.', handler () { const prev = findTab(-1) prev && switchTab(prev) @@ -244,6 +246,8 @@ export default defineComponent({ name: 'file-tabs.switch-right', description: t('command-desc.file-tabs_switch-right'), forUser: true, + forMcp: true, + mcpDescription: 'Switch to right tab. No args. No return.', handler () { const next = findTab(1) next && switchTab(next) @@ -256,6 +260,8 @@ export default defineComponent({ description: t('command-desc.file-tabs_close-current'), handler: closeCurrent, forUser: true, + forMcp: true, + mcpDescription: 'Close current tab. No args. No return.', keys: isElectron ? [CtrlCmd, 'w'] : [CtrlCmd, Alt, 'w'] }) @@ -263,6 +269,8 @@ export default defineComponent({ name: 'file-tabs.search-tabs', description: t('command-desc.file-tabs_search-tabs'), forUser: true, + forMcp: true, + mcpDescription: 'Search tabs. No args. No return.', handler () { refTabs.value?.showQuickFilter() }, @@ -273,6 +281,8 @@ export default defineComponent({ name: 'file-tabs.show-welcome', description: t('command-desc.file-tabs_show-welcome'), forUser: true, + forMcp: true, + mcpDescription: 'Show welcome page. No args. No return.', handler () { switchDoc(null) }, diff --git a/src/renderer/components/Filter.vue b/src/renderer/components/Filter.vue index 6de10637..e1ca1c59 100644 --- a/src/renderer/components/Filter.vue +++ b/src/renderer/components/Filter.vue @@ -105,6 +105,8 @@ export default defineComponent({ description: t('command-desc.workbench_show-quick-open'), handler: showQuickOpen, forUser: true, + forMcp: true, + mcpDescription: 'Show quick open dialog. No args. No return.', keys: [CtrlCmd, 'p'] }) registerAction({ name: 'filter.choose-document', handler: chooseDocument }) diff --git a/src/renderer/components/FindInPreview.vue b/src/renderer/components/FindInPreview.vue index 5525c2f8..1f61fbd1 100644 --- a/src/renderer/components/FindInPreview.vue +++ b/src/renderer/components/FindInPreview.vue @@ -200,6 +200,8 @@ registerAction({ keys: [CtrlCmd, 'f'], handler: show, forUser: true, + forMcp: true, + mcpDescription: 'Show find widget in preview. No args. No return.', when: () => { return !(getEditor()?.hasTextFocus()) && store.state.showView } diff --git a/src/renderer/components/KeyboardShortcuts.vue b/src/renderer/components/KeyboardShortcuts.vue index 2a031f9a..4b749e37 100644 --- a/src/renderer/components/KeyboardShortcuts.vue +++ b/src/renderer/components/KeyboardShortcuts.vue @@ -369,7 +369,9 @@ registerAction({ name: 'keyboard-shortcuts.show-manager', description: t('command-desc.keyboard-shortcuts_show-manager'), handler: show, - forUser: true + forUser: true, + forMcp: true, + mcpDescription: 'Show keyboard shortcuts manager. No args. No return.' }) onUnmounted(() => { diff --git a/src/renderer/components/SearchPanel.vue b/src/renderer/components/SearchPanel.vue index 4668c227..719a7a41 100644 --- a/src/renderer/components/SearchPanel.vue +++ b/src/renderer/components/SearchPanel.vue @@ -786,6 +786,8 @@ registerAction({ keys: [CtrlCmd, Shift, 'f'], description: t('command-desc.base_find-in-repository'), forUser: true, + forMcp: true, + mcpDescription: 'Find text in repository. Args: [query?:{pattern?:string, caseSensitive?:boolean, wholeWord?:boolean, regExp?:boolean, include?:string, exclude?:string}]. No return.', handler: (query?: FindInRepositoryQuery) => { visible.value = true diff --git a/src/renderer/components/Tree.vue b/src/renderer/components/Tree.vue index 2b9c4331..0f1bcf45 100644 --- a/src/renderer/components/Tree.vue +++ b/src/renderer/components/Tree.vue @@ -96,6 +96,8 @@ export default defineComponent({ name: 'tree.reveal-current-node', description: t('command-desc.tree_reveal-current-node'), forUser: true, + forMcp: true, + mcpDescription: 'Reveal current document in tree. No args. No return.', handler: revealCurrentNode }) diff --git a/src/renderer/plugins/copy-content.tsx b/src/renderer/plugins/copy-content.tsx index 37bfc0e6..b41c37a6 100644 --- a/src/renderer/plugins/copy-content.tsx +++ b/src/renderer/plugins/copy-content.tsx @@ -142,6 +142,8 @@ export default { description: ctx.i18n.t('command-desc.plugin_copy-content_copy-content'), handler: copyContent, forUser: true, + forMcp: true, + mcpDescription: 'Copy content in various formats. No args. No return.', keys: [ctx.keybinding.CtrlCmd, ctx.keybinding.Shift, 'c'], }) diff --git a/src/renderer/plugins/editor-markdown.ts b/src/renderer/plugins/editor-markdown.ts index 938d9616..e32ca379 100644 --- a/src/renderer/plugins/editor-markdown.ts +++ b/src/renderer/plugins/editor-markdown.ts @@ -192,6 +192,8 @@ export default { description: ctx.i18n.t('command-desc.plugin_editor_focus-editor'), handler: focusEditor, forUser: true, + forMcp: true, + mcpDescription: 'Focus editor. No args. No return.', keys: [ctx.keybinding.Shift, ctx.keybinding.Alt, 'x'] }) } diff --git a/src/renderer/plugins/electron-zoom.ts b/src/renderer/plugins/electron-zoom.ts index 44facc1b..6b25cded 100644 --- a/src/renderer/plugins/electron-zoom.ts +++ b/src/renderer/plugins/electron-zoom.ts @@ -34,6 +34,8 @@ export default { keys: [ctx.keybinding.CtrlCmd, '='], description: ctx.i18n.t('command-desc.plugin_electron-zoom_zoom-in'), forUser: true, + forMcp: true, + mcpDescription: 'Zoom in. No args. No return.', handler: () => changeZoomFactor(true) }) @@ -42,6 +44,8 @@ export default { keys: [ctx.keybinding.CtrlCmd, '-'], description: ctx.i18n.t('command-desc.plugin_electron-zoom_zoom-out'), forUser: true, + forMcp: true, + mcpDescription: 'Zoom out. No args. No return.', handler: () => changeZoomFactor(false) }) @@ -50,6 +54,8 @@ export default { keys: [ctx.keybinding.CtrlCmd, '0'], description: ctx.i18n.t('command-desc.plugin_electron-zoom_zoom-reset'), forUser: true, + forMcp: true, + mcpDescription: 'Reset zoom. No args. No return.', handler: () => setZoomFactor(1) }) diff --git a/src/renderer/plugins/history-stack.ts b/src/renderer/plugins/history-stack.ts index 9f7eeb47..97567b5a 100644 --- a/src/renderer/plugins/history-stack.ts +++ b/src/renderer/plugins/history-stack.ts @@ -141,6 +141,8 @@ export default { name: backId, description: ctx.i18n.t('command-desc.plugin_document-history-stack_back'), forUser: true, + forMcp: true, + mcpDescription: 'Go back in document history. No args. No return.', handler: () => go(-1), keys: [ctx.keybinding.Alt, ctx.keybinding.BracketLeft], }) @@ -149,6 +151,8 @@ export default { name: forwardId, description: ctx.i18n.t('command-desc.plugin_document-history-stack_forward'), forUser: true, + forMcp: true, + mcpDescription: 'Go forward in document history. No args. No return.', handler: () => go(1), keys: [ctx.keybinding.Alt, ctx.keybinding.BracketRight], }) diff --git a/src/renderer/plugins/image-localization.ts b/src/renderer/plugins/image-localization.ts index 4d1f7f7b..06a85512 100644 --- a/src/renderer/plugins/image-localization.ts +++ b/src/renderer/plugins/image-localization.ts @@ -126,6 +126,8 @@ export default { handler: transformAll, description: ctx.i18n.t('command-desc.plugin_image-localization_all'), forUser: true, + forMcp: true, + mcpDescription: 'Transform remote images to local. No args. No return.', when, }) diff --git a/src/renderer/plugins/insert-table/index.ts b/src/renderer/plugins/insert-table/index.ts index cc965b52..5a722585 100644 --- a/src/renderer/plugins/insert-table/index.ts +++ b/src/renderer/plugins/insert-table/index.ts @@ -111,6 +111,8 @@ export default { ctx.action.registerAction({ name: actionName, forUser: true, + forMcp: true, + mcpDescription: 'Insert table. No args. No return.', description: ctx.i18n.t('insert-table.insert-table'), handler: () => { if (canInsertTable()) { diff --git a/src/renderer/plugins/status-bar-premium.ts b/src/renderer/plugins/status-bar-premium.ts index 5b06a171..f1669dbe 100644 --- a/src/renderer/plugins/status-bar-premium.ts +++ b/src/renderer/plugins/status-bar-premium.ts @@ -83,6 +83,8 @@ export default { name: actionName, description: ctx.i18n.t('premium.confetti'), forUser: true, + forMcp: true, + mcpDescription: 'Show confetti celebration. No args. No return.', handler }) } diff --git a/src/renderer/plugins/status-bar-repository-switch.ts b/src/renderer/plugins/status-bar-repository-switch.ts index 4b03a072..d00450ad 100644 --- a/src/renderer/plugins/status-bar-repository-switch.ts +++ b/src/renderer/plugins/status-bar-repository-switch.ts @@ -78,6 +78,8 @@ export default { name: `base.switch-repository-${i}`, description: i === 0 ? ctx.i18n.t('switch-the-last-repo') : ctx.i18n.t('switch-repo-n', String(i)), forUser: true, + forMcp: true, + mcpDescription: `Switch to repository ${i === 0 ? 'last' : i}. No args. No return.`, keys: [ctx.keybinding.Alt, String(i)], handler: () => { const repos = ctx.repo.getAllRepos() diff --git a/src/renderer/plugins/status-bar-theme.ts b/src/renderer/plugins/status-bar-theme.ts index 08930a70..2d4bb9a1 100644 --- a/src/renderer/plugins/status-bar-theme.ts +++ b/src/renderer/plugins/status-bar-theme.ts @@ -50,6 +50,8 @@ export default { handler, description: ctx.i18n.t('command-desc.plugin_status-bar-theme_switch'), forUser: true, + forMcp: true, + mcpDescription: 'Switch theme. No args. No return.', }) } } as Plugin diff --git a/src/renderer/plugins/switch-todo.ts b/src/renderer/plugins/switch-todo.ts index ec583b0c..cbad879f 100644 --- a/src/renderer/plugins/switch-todo.ts +++ b/src/renderer/plugins/switch-todo.ts @@ -33,6 +33,8 @@ export default { description: ctx.i18n.t('command-desc.plugin_switch-todo_switch'), keys: [ctx.keybinding.Alt, 'o'], forUser: true, + forMcp: true, + mcpDescription: 'Switch todo status. Args: [line:number?, checked:boolean?]. No return.', handler: (line?: number, checked?: boolean) => { if (line) { switchTodo(line, checked) diff --git a/src/renderer/plugins/view-links/index.ts b/src/renderer/plugins/view-links/index.ts index 3c7d62ed..251b585f 100644 --- a/src/renderer/plugins/view-links/index.ts +++ b/src/renderer/plugins/view-links/index.ts @@ -39,6 +39,8 @@ export default { ctx.action.registerAction({ name: actionName, forUser: true, + forMcp: true, + mcpDescription: 'View document links. No args. No return.', description: ctx.i18n.t('command-desc.plugin_view-links_view-document-links'), handler: showLinks, when, diff --git a/src/renderer/services/editor.ts b/src/renderer/services/editor.ts index e9cb3c82..00796cbc 100644 --- a/src/renderer/services/editor.ts +++ b/src/renderer/services/editor.ts @@ -579,8 +579,10 @@ export async function isDirty (): Promise { registerAction({ name: 'editor.toggle-wrap', description: t('command-desc.editor_toggle-wrap'), + mcpDescription: 'Toggle word wrap. No args. No return.', handler: toggleWrap, forUser: true, + forMcp: true, keys: [Alt, 'w'] }) diff --git a/src/renderer/services/layout.ts b/src/renderer/services/layout.ts index 851bbcb6..d059496b 100644 --- a/src/renderer/services/layout.ts +++ b/src/renderer/services/layout.ts @@ -117,31 +117,39 @@ export function toggleEditorPreviewExclusive (exclusive?: boolean) { registerAction({ name: 'layout.toggle-side', description: t('command-desc.layout_toggle-side'), + mcpDescription: 'Toggle side panel. Args: [visible:boolean?]. No return.', handler: toggleSide, forUser: true, + forMcp: true, keys: [Alt, 'e'] }) registerAction({ name: 'layout.toggle-editor', description: t('command-desc.layout_toggle-editor'), + mcpDescription: 'Toggle editor panel. Args: [visible:boolean?]. No return.', handler: toggleEditor, forUser: true, + forMcp: true, keys: [Alt, 'x'] }) registerAction({ name: 'layout.toggle-view', description: t('command-desc.layout_toggle-view'), + mcpDescription: 'Toggle preview panel. Args: [visible:boolean?]. No return.', handler: toggleView, forUser: true, + forMcp: true, keys: [Alt, 'v'] }) registerAction({ name: 'layout.toggle-xterm', description: t('command-desc.layout_toggle-xterm'), + mcpDescription: 'Toggle terminal panel. Args: [visible:boolean?]. No return.', handler: toggleXterm, forUser: true, + forMcp: true, keys: [Alt, 't'] }) diff --git a/src/renderer/services/tree.ts b/src/renderer/services/tree.ts index 6f67e739..343e2fe4 100644 --- a/src/renderer/services/tree.ts +++ b/src/renderer/services/tree.ts @@ -100,6 +100,8 @@ store.watch(() => store.state.treeSort, async () => { registerAction({ name: 'tree.refresh', description: t('command-desc.tree_refresh'), + mcpDescription: 'Refresh file tree. No args. No return.', forUser: true, + forMcp: true, handler: refreshTree, }) diff --git a/src/renderer/services/view.ts b/src/renderer/services/view.ts index ff7855e9..7b9b6b77 100644 --- a/src/renderer/services/view.ts +++ b/src/renderer/services/view.ts @@ -542,7 +542,9 @@ registerHook('VIEW_RENDER_IFRAME_READY', ({ iframe }) => { registerAction({ name: 'view.enter-presentation', forUser: true, + forMcp: true, description: t('command-desc.view_enter-presentation'), + mcpDescription: 'Enter presentation mode. No args. No return.', handler: () => present(true), keys: ['F5'] }) diff --git a/src/renderer/services/workbench.ts b/src/renderer/services/workbench.ts index 94b8e951..7aafd4bf 100644 --- a/src/renderer/services/workbench.ts +++ b/src/renderer/services/workbench.ts @@ -18,7 +18,9 @@ export function toggleOutline (visible?: boolean) { registerAction({ name: 'workbench.toggle-outline', description: t('command-desc.workbench_toggle-outline'), + mcpDescription: 'Toggle outline panel. Args: [visible:boolean?]. No return.', forUser: true, + forMcp: true, handler: toggleOutline, keys: [Shift, Alt, 'o'] }) diff --git a/src/renderer/startup.ts b/src/renderer/startup.ts index 0115ddc9..d0f1cf7d 100644 --- a/src/renderer/startup.ts +++ b/src/renderer/startup.ts @@ -299,8 +299,10 @@ registerHook('RIGHT_SIDE_PANEL_CHANGE', ({ type }) => { registerAction({ name: 'layout.toggle-content-right-side', description: t('command-desc.layout_toggle-content-right-side'), + mcpDescription: 'Toggle content right panel. Args: [visible:boolean?]. No return.', handler: ctx.layout.toggleContentRightSide, forUser: true, + forMcp: true, when () { return ctx.workbench.ContentRightSide.getAllPanels().length > 0 },