From 5b77ba4a3e994fb956a752bfd34d2d00ff35a825 Mon Sep 17 00:00:00 2001 From: purocean Date: Tue, 7 Jan 2025 13:22:13 +0800 Subject: [PATCH] feat(text-comparator): add text comparator plugin and integrate with editor --- src/renderer/components/FileTabs.vue | 4 +- src/renderer/plugins.ts | 2 + src/renderer/plugins/text-comparator.ts | 69 +++++++++++++++++++++++++ src/renderer/services/document.ts | 8 +-- src/renderer/services/editor.ts | 9 +++- src/renderer/types.ts | 1 + src/share/i18n/languages/en.ts | 2 + src/share/i18n/languages/ru.ts | 2 + src/share/i18n/languages/zh-CN.ts | 2 + src/share/i18n/languages/zh-TW.ts | 2 + 10 files changed, 93 insertions(+), 8 deletions(-) create mode 100644 src/renderer/plugins/text-comparator.ts diff --git a/src/renderer/components/FileTabs.vue b/src/renderer/components/FileTabs.vue index 2de74a9c5..48a4874a3 100644 --- a/src/renderer/components/FileTabs.vue +++ b/src/renderer/components/FileTabs.vue @@ -338,7 +338,7 @@ export default defineComponent({ const fileTabs = computed(() => (tabs.value as Components.FileTabs.Item[]).map(tab => { if (currentFile.value && tab.key === toUri(currentFile.value)) { - const { status, writeable } = currentFile.value + const { type, status, writeable } = currentFile.value let mark = '' if (!isSaved.value) { @@ -351,7 +351,7 @@ export default defineComponent({ mark = '!' } else if (status === 'loaded') { mark = '' - } else { + } else if (type === 'file') { mark = '…' } diff --git a/src/renderer/plugins.ts b/src/renderer/plugins.ts index f01629b22..1f5070002 100644 --- a/src/renderer/plugins.ts +++ b/src/renderer/plugins.ts @@ -76,6 +76,7 @@ import recordRecentDocument from '@fe/plugins/record-recent-document' import aiCopilot from '@fe/plugins/ai-copilot' import viewLinks from '@fe/plugins/view-links' import insertTable from '@fe/plugins/insert-table' +import textComparator from '@fe/plugins/text-comparator' export default [ buildInRenderers, @@ -156,4 +157,5 @@ export default [ aiCopilot, viewLinks, insertTable, + textComparator, ] diff --git a/src/renderer/plugins/text-comparator.ts b/src/renderer/plugins/text-comparator.ts new file mode 100644 index 000000000..ee4bfe4a6 --- /dev/null +++ b/src/renderer/plugins/text-comparator.ts @@ -0,0 +1,69 @@ +import { Plugin } from '@fe/context' +import type { BuildInActions } from '@fe/types' + +export default { + name: 'text-comparator', + register: ctx => { + const extensionId = '@yank-note/extension-text-comparator' + const editorDocType = '__comparator' as const + const compareTextActionId: keyof BuildInActions = 'plugin.text-comparator.open-text-comparator' + + const TextComparator = ctx.lib.vue.defineComponent({ + setup () { + const { h } = ctx.lib.vue + return () => h( + 'div', + { style: 'width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;' }, + [ + h('a', { + href: 'javascript:void(0)', + onClick: () => { + ctx.showExtensionManager(extensionId) + }, + }, ctx.i18n.t('install-extension-tips', extensionId)) + ] + ) + } + }) + + ctx.editor.registerCustomEditor({ + name: 'text-comparator', + displayName: 'Text Comparator', + supportNonNormalFile: true, + component: TextComparator, + hiddenPreview: true, + when: ({ doc }) => { + return doc?.type === editorDocType + } + }) + + ctx.action.registerAction({ + name: compareTextActionId, + description: ctx.i18n.t('status-bar.tool.open-text-comparator'), + forUser: true, + handler: () => { + ctx.doc.switchDoc({ + type: editorDocType, + name: 'Text Comparator', + path: '', + repo: ctx.store.state.currentRepo?.name || '' + }) + }, + }) + + ctx.statusBar.tapMenus(menus => { + menus['status-bar-tool']?.list?.push( + { + id: compareTextActionId, + type: 'normal', + title: ctx.i18n.t('status-bar.tool.open-text-comparator'), + subTitle: ctx.keybinding.getKeysLabel(compareTextActionId), + onClick: () => { + ctx.action.getActionHandler(compareTextActionId)() + }, + order: 100, + }, + ) + }) + } +} as Plugin diff --git a/src/renderer/services/document.ts b/src/renderer/services/document.ts index 31739d525..c7238d58d 100644 --- a/src/renderer/services/document.ts +++ b/src/renderer/services/document.ts @@ -716,7 +716,7 @@ async function _switchDoc (doc: Doc | null, opts?: SwitchDocOpts): Promise logger.debug('switchDoc', doc) - if (doc && doc.type !== 'file') { + if (doc && doc.type === 'dir') { throw new Error('Invalid document type') } @@ -739,15 +739,15 @@ async function _switchDoc (doc: Doc | null, opts?: SwitchDocOpts): Promise }) if (doc) { - doc.plain = !!(extensions.supported(doc.name) || resolveDocType(doc.name)?.type?.plain) + doc.plain = doc.type === 'file' && (!!(extensions.supported(doc.name) || resolveDocType(doc.name)?.type?.plain)) doc.absolutePath = getAbsolutePath(doc) } await triggerHook('DOC_BEFORE_SWITCH', { doc, opts }, { breakable: true, ignoreError: true }) try { - if (!doc) { - store.state.currentFile = null + if (!doc || doc.type !== 'file') { + store.state.currentFile = doc store.state.currentContent = '' triggerHook('DOC_SWITCHED', { doc: null, opts }) return diff --git a/src/renderer/services/editor.ts b/src/renderer/services/editor.ts index c1fa8f3e5..fdaf9cc2b 100644 --- a/src/renderer/services/editor.ts +++ b/src/renderer/services/editor.ts @@ -456,15 +456,20 @@ export function switchEditor (name: string) { /** * Register a custom editor. * @param editor Editor + * @param override Override the existing editor */ -export function registerCustomEditor (editor: CustomEditor) { +export function registerCustomEditor (editor: CustomEditor, override = false) { if (!editor.component && editor.name !== DEFAULT_MARKDOWN_EDITOR_NAME) { throw new Error('Editor component is required') } // check if the editor is already registered if (ioc.get('EDITOR_CUSTOM_EDITOR').some(item => item.name === editor.name)) { - throw new Error(`Editor ${editor.name} is already registered`) + if (override) { + removeCustomEditor(editor.name) + } else { + throw new Error(`Editor ${editor.name} is already registered`) + } } ioc.register('EDITOR_CUSTOM_EDITOR', editor) diff --git a/src/renderer/types.ts b/src/renderer/types.ts index b12e4eb37..54e2463c0 100644 --- a/src/renderer/types.ts +++ b/src/renderer/types.ts @@ -441,6 +441,7 @@ export type BuildInActions = { 'plugin.electron-zoom.zoom-out': () => void, 'plugin.electron-zoom.zoom-reset': () => void, 'plugin.view-links.view-document-links': () => void, + 'plugin.text-comparator.open-text-comparator': () => void, 'premium.show': (tab?: PremiumTab) => void, 'base.find-in-repository': (query?: FindInRepositoryQuery) => void, 'base.switch-repository-1': () => void, diff --git a/src/share/i18n/languages/en.ts b/src/share/i18n/languages/en.ts index 9d0f834bb..21a789a72 100644 --- a/src/share/i18n/languages/en.ts +++ b/src/share/i18n/languages/en.ts @@ -47,6 +47,7 @@ const data = { 'read-only-mode-desc': 'The application is currently in read-only mode and cannot be edited.', 'trigger-suggestions': 'Trigger Suggestions', 'table-of-contents': 'Table of Contents', + 'text-comparator': 'Text Comparator', 'premium': { 'confetti': 'Confetti', 'need-purchase': '[%s] Premium is required', @@ -265,6 +266,7 @@ const data = { 'share-preview': 'Share Preview', 'print': 'Print Document', 'export': 'Export Document', + 'open-text-comparator': 'Open Text Comparator', }, 'document-info': { 'selected': 'Selected', diff --git a/src/share/i18n/languages/ru.ts b/src/share/i18n/languages/ru.ts index 7c4215252..4b7ed9642 100644 --- a/src/share/i18n/languages/ru.ts +++ b/src/share/i18n/languages/ru.ts @@ -48,6 +48,7 @@ const data: BaseLanguage = { 'read-only-mode-desc': 'В настоящее время приложение находится в режиме "только для чтения".', 'trigger-suggestions': 'Подсказки триггера', 'table-of-contents': 'Содержание', + 'text-comparator': 'Сравнение текста', 'premium': { 'confetti': 'Конфетти', 'need-purchase': 'Для [%s] требуется премиум', @@ -266,6 +267,7 @@ const data: BaseLanguage = { 'share-preview': 'Поделиться', 'print': 'Печать документа', 'export': 'Экспорт документа', + 'open-text-comparator': 'Открыть сравнение текста', }, 'document-info': { 'selected': 'Выделено', diff --git a/src/share/i18n/languages/zh-CN.ts b/src/share/i18n/languages/zh-CN.ts index f023bae7b..d5c4f4e2a 100644 --- a/src/share/i18n/languages/zh-CN.ts +++ b/src/share/i18n/languages/zh-CN.ts @@ -48,6 +48,7 @@ const data: BaseLanguage = { 'read-only-mode-desc': '当前是只读模式,不可编辑', 'trigger-suggestions': '触发提示', 'table-of-contents': '目录', + 'text-comparator': '文本比较器', 'premium': { 'confetti': '彩色纸屑', 'need-purchase': '[%s] 需要高级版', @@ -266,6 +267,7 @@ const data: BaseLanguage = { 'share-preview': '分享预览', 'print': '打印文档', 'export': '导出文档', + 'open-text-comparator': '打开文本比较器', }, 'document-info': { 'selected': '已选择', diff --git a/src/share/i18n/languages/zh-TW.ts b/src/share/i18n/languages/zh-TW.ts index d335ab39b..6c98df432 100644 --- a/src/share/i18n/languages/zh-TW.ts +++ b/src/share/i18n/languages/zh-TW.ts @@ -48,6 +48,7 @@ const data: BaseLanguage = { 'read-only-mode-desc': '當前應用處於唯讀模式,不可編輯', 'trigger-suggestions': '觸發提示', 'table-of-contents': '目錄', + 'text-comparator': '文本比較器', 'premium': { 'confetti': '彩色紙屑', 'need-purchase': '[%s] 需高級版', @@ -266,6 +267,7 @@ const data: BaseLanguage = { 'share-preview': '分享預覽', 'print': '打印文檔', 'export': '導出文檔', + 'open-text-comparator': '打開文本比較器', }, 'document-info': { 'selected': '已選擇',