diff --git a/src/components/apply-view/ApplyViewRoot.tsx b/src/components/apply-view/ApplyViewRoot.tsx index 3dd1b4d..738b651 100644 --- a/src/components/apply-view/ApplyViewRoot.tsx +++ b/src/components/apply-view/ApplyViewRoot.tsx @@ -5,11 +5,9 @@ import ContentEditable from 'react-contenteditable' import { ApplyViewState } from '../../ApplyView' import { useApp } from '../../contexts/AppContext' +import { t } from '../../lang/helpers' -export default function ApplyViewRoot({ - state, - close, -}: { +export default function ApplyViewRoot({ state, close }: { state: ApplyViewState close: () => void }) { @@ -29,14 +27,14 @@ export default function ApplyViewRoot({ // Track which lines have been accepted or excluded const [diffStatus, setDiffStatus] = useState>([]) - + const [diff] = useState(() => { const initialDiff = diffLines(state.oldContent, state.newContent) // Initialize all lines as 'active' setDiffStatus(initialDiff.map(() => 'active')) return initialDiff }) - + // Store edited content for each diff part const [editedContents, setEditedContents] = useState( diff.map(part => part.value) @@ -46,170 +44,170 @@ export default function ApplyViewRoot({ // Filter and process content based on diffStatus const newContent = diff.reduce((result, change, index) => { // Keep unchanged content, non-excluded additions, or excluded removals - if ((!change.added && !change.removed) || - (change.added && diffStatus[index] !== 'excluded') || + if ((!change.added && !change.removed) || + (change.added && diffStatus[index] !== 'excluded') || (change.removed && diffStatus[index] === 'excluded')) { return result + editedContents[index]; } return result; }, '') const file = app.vault.getFileByPath(state.file) - if (!file) { - throw new Error('File not found') - } - await app.vault.modify(file, newContent) - if (state.onClose) { - state.onClose(true) - } - close() - } + if (!file) { + throw new Error(String(t('applyView.fileNotFound'))) + } + await app.vault.modify(file, newContent) + if (state.onClose) { + state.onClose(true) + } + close() + } - const handleReject = async () => { - if (state.onClose) { - state.onClose(false) - } - close() - } + const handleReject = async () => { + if (state.onClose) { + state.onClose(false) + } + close() + } - const excludeDiffLine = (index: number) => { - setDiffStatus(prevStatus => { - const newStatus = [...prevStatus] - // Mark line as excluded - newStatus[index] = 'excluded' - return newStatus - }) - } + const excludeDiffLine = (index: number) => { + setDiffStatus(prevStatus => { + const newStatus = [...prevStatus] + // Mark line as excluded + newStatus[index] = 'excluded' + return newStatus + }) + } - const acceptDiffLine = (index: number) => { - setDiffStatus(prevStatus => { - const newStatus = [...prevStatus] - // Mark line as accepted - newStatus[index] = 'accepted' - return newStatus - }) - } + const acceptDiffLine = (index: number) => { + setDiffStatus(prevStatus => { + const newStatus = [...prevStatus] + // Mark line as accepted + newStatus[index] = 'accepted' + return newStatus + }) + } - const handleKeyDown = (event: KeyboardEvent) => { - const modifierKey = Platform.isMacOS ? event.metaKey : event.ctrlKey; - if (modifierKey) { - if (event.key === 'Enter') { - event.preventDefault(); - event.stopPropagation(); - handleAccept(); - } else if (event.key === 'Backspace') { - event.preventDefault(); - event.stopPropagation(); - handleReject(); - } - } - } - - // Handle content editing changes - const handleContentChange = (index: number, evt: { target: { value: string } }) => { - const newEditedContents = [...editedContents]; - newEditedContents[index] = evt.target.value; - setEditedContents(newEditedContents); - } + const handleKeyDown = (event: KeyboardEvent) => { + const modifierKey = Platform.isMacOS ? event.metaKey : event.ctrlKey; + if (modifierKey) { + if (event.key === 'Enter') { + event.preventDefault(); + event.stopPropagation(); + handleAccept(); + } else if (event.key === 'Backspace') { + event.preventDefault(); + event.stopPropagation(); + handleReject(); + } + } + } - // Add event listeners on mount and remove on unmount - useEffect(() => { - const handler = (e: KeyboardEvent) => handleKeyDown(e); - window.addEventListener('keydown', handler, true); - return () => { - window.removeEventListener('keydown', handler, true); - } - }, [handleAccept, handleReject]) // Dependencies for the effect + // Handle content editing changes + const handleContentChange = (index: number, evt: { target: { value: string } }) => { + const newEditedContents = [...editedContents]; + newEditedContents[index] = evt.target.value; + setEditedContents(newEditedContents); + } - return ( -
-
-
-
-
-
-
- Applying: {state?.file ?? ''} -
-
- - -
-
-
+ // Add event listeners on mount and remove on unmount + useEffect(() => { + const handler = (e: KeyboardEvent) => handleKeyDown(e); + window.addEventListener('keydown', handler, true); + return () => { + window.removeEventListener('keydown', handler, true); + } + }, [handleAccept, handleReject]) // Dependencies for the effect -
-
-
-
-
-
- {state?.file - ? state.file.replace(/\.[^/.]+$/, '') - : ''} -
+ return ( +
+
+
+
+
+
+
+ {t('applyView.applyingFile').replace('{{file}}', state?.file ?? '')} +
+
+ + +
+
+
- {diff.map((part, index) => { - // Determine line display status based on diffStatus - const status = diffStatus[index] - const isHidden = - (part.added && status === 'excluded') || - (part.removed && status === 'accepted') - - if (isHidden) return null +
+
+
+
+
+
+ {state?.file + ? state.file.replace(/\.[^/.]+$/, '') + : ''} +
- return ( -
-
- handleContentChange(index, evt)} - className="infio-editable-content" - /> - {(part.added || part.removed) && status === 'active' && ( -
- - -
- )} -
-
- ) - })} -
-
-
-
-
- -
- ) +
+ ) } diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index b8c5ce2..fd72e41 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -138,5 +138,40 @@ export default { "updateCommand": "Update Command", "errorContentRequired": "Please enter a content for your template", "errorNameRequired": "Please enter a name for your template" + }, + main: { + openNewChat: "Open new chat", + openInfioCopilot: 'Open infio copilot', + addSelectionToChat: 'Add selection to chat', + rebuildVaultIndex: 'Rebuild entire vault index', + updateVaultIndex: 'Update index for modified files', + autocompleteAccept: 'Autocomplete accept', + autocompletePredict: 'Autocomplete predict', + autocompleteToggle: 'Autocomplete toggle', + autocompleteEnable: 'Autocomplete enable', + autocompleteDisable: 'Autocomplete disable', + inlineEditCommand: 'Inline edit', + }, + notifications: { + rebuildingIndex: 'Rebuilding vault index...', + indexingChunks: 'Indexing chunks: {completedChunks} / {totalChunks}', + rebuildComplete: 'Rebuilding vault index complete', + rebuildFailed: 'Rebuilding vault index failed', + updatingIndex: 'Updating vault index...', + updateComplete: 'Vault index updated', + updateFailed: 'Vault index update failed', + selectTextFirst: 'Please select some text first', + migrationFailed: 'Failed to migrate to JSON storage. Please check the console for details.', + reloadingInfio: 'Reloading "infio" due to migration', + }, + applyView: { + applyingFile: 'Applying: {{file}}', + acceptChanges: 'Accept changes', + acceptAll: 'Accept All {{shortcut}}', + rejectChanges: 'Reject changes', + rejectAll: 'Reject All {{shortcut}}', + fileNotFound: 'File not found', + acceptLine: 'Accept line', + excludeLine: 'Exclude line', } } diff --git a/src/lang/locale/zh-cn.ts b/src/lang/locale/zh-cn.ts index 267aade..f97fbc7 100644 --- a/src/lang/locale/zh-cn.ts +++ b/src/lang/locale/zh-cn.ts @@ -141,5 +141,40 @@ export default { "updateCommand": "更新命令", "errorContentRequired": "请输入模板内容", "errorNameRequired": "请输入模板名称" + }, + main: { + openNewChat: "打开新聊天", + openInfioCopilot: '打开 Infio Copilot', + addSelectionToChat: '将选定内容添加到聊天', + rebuildVaultIndex: '重建整个 Vault 索引', + updateVaultIndex: '更新已修改文件的索引', + autocompleteAccept: '接受自动完成', + autocompletePredict: '手动触发自动完成', + autocompleteToggle: '切换自动完成', + autocompleteEnable: '启用自动完成', + autocompleteDisable: '禁用自动完成', + inlineEditCommand: '文本内编辑', + }, + notifications: { + rebuildingIndex: '正在重建 Vault 索引...', + indexingChunks: '正在索引块:{completedChunks} / {totalChunks}', + rebuildComplete: 'Vault 索引重建完成', + rebuildFailed: 'Vault 索引重建失败', + updatingIndex: '正在更新 Vault 索引...', + updateComplete: 'Vault 索引已更新', + updateFailed: 'Vault 索引更新失败', + selectTextFirst: '请先选择一些文本', + migrationFailed: '迁移到 JSON 存储失败。请检查控制台以获取详细信息。', + reloadingInfio: '因迁移而重新加载 "infio"', + }, + applyView: { + applyingFile: '正在应用: {{file}}', + acceptChanges: '接受更改', + acceptAll: '全部接受 {{shortcut}}', + rejectChanges: '拒绝更改', + rejectAll: '全部拒绝 {{shortcut}}', + fileNotFound: '文件未找到', + acceptLine: '接受此行', + excludeLine: '排除此行', } }; diff --git a/src/main.ts b/src/main.ts index f6452dc..5862cb0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -25,6 +25,7 @@ import RenderSuggestionPlugin from "./render-plugin/render-surgestion-plugin" import { InlineSuggestionState } from "./render-plugin/states" import { InfioSettingTab } from './settings/SettingTab' import StatusBar from "./status-bar" +import { t } from './lang/helpers' import { InfioSettings, parseInfioSettings, @@ -32,7 +33,6 @@ import { import { getMentionableBlockData } from './utils/obsidian' import './utils/path' - export default class InfioPlugin extends Plugin { private metadataCacheUnloadFn: (() => void) | null = null private activeLeafChangeUnloadFn: (() => void) | null = null @@ -56,7 +56,7 @@ export default class InfioPlugin extends Plugin { this.addSettingTab(this.settingTab) // add icon to ribbon - this.addRibbonIcon('wand-sparkles', 'Open infio copilot', () => + this.addRibbonIcon('wand-sparkles', t('main.openInfioCopilot'), () => this.openChatView(), ) @@ -163,13 +163,13 @@ export default class InfioPlugin extends Plugin { /// *** Commands *** this.addCommand({ id: 'open-new-chat', - name: 'Open new chat', + name: t('main.openNewChat'), callback: () => this.openChatView(true), }) this.addCommand({ id: 'add-selection-to-chat', - name: 'Add selection to chat', + name: t('main.addSelectionToChat'), editorCallback: (editor: Editor, view: MarkdownView) => { this.addSelectionToChat(editor, view) }, @@ -183,9 +183,9 @@ export default class InfioPlugin extends Plugin { this.addCommand({ id: 'rebuild-vault-index', - name: 'Rebuild entire vault index', + name: t('main.rebuildVaultIndex'), callback: async () => { - const notice = new Notice('Rebuilding vault index...', 0) + const notice = new Notice(t('notifications.rebuildingIndex'), 0) try { const ragEngine = await this.getRAGEngine() await ragEngine.updateVaultIndex( @@ -195,15 +195,15 @@ export default class InfioPlugin extends Plugin { const { completedChunks, totalChunks } = queryProgress.indexProgress notice.setMessage( - `Indexing chunks: ${completedChunks} / ${totalChunks}`, + t('notifications.indexingChunks', { completedChunks, totalChunks }), ) } }, ) - notice.setMessage('Rebuilding vault index complete') + notice.setMessage(t('notifications.rebuildComplete')) } catch (error) { console.error(error) - notice.setMessage('Rebuilding vault index failed') + notice.setMessage(t('notifications.rebuildFailed')) } finally { setTimeout(() => { notice.hide() @@ -214,9 +214,9 @@ export default class InfioPlugin extends Plugin { this.addCommand({ id: 'update-vault-index', - name: 'Update index for modified files', + name: t('main.updateVaultIndex'), callback: async () => { - const notice = new Notice('Updating vault index...', 0) + const notice = new Notice(t('notifications.updatingIndex'), 0) try { const ragEngine = await this.getRAGEngine() await ragEngine.updateVaultIndex( @@ -226,15 +226,15 @@ export default class InfioPlugin extends Plugin { const { completedChunks, totalChunks } = queryProgress.indexProgress notice.setMessage( - `Indexing chunks: ${completedChunks} / ${totalChunks}`, + t('notifications.indexingChunks', { completedChunks, totalChunks }), ) } }, ) - notice.setMessage('Vault index updated') + notice.setMessage(t('notifications.updateComplete')) } catch (error) { console.error(error) - notice.setMessage('Vault index update failed') + notice.setMessage(t('notifications.updateFailed')) } finally { setTimeout(() => { notice.hide() @@ -245,7 +245,7 @@ export default class InfioPlugin extends Plugin { this.addCommand({ id: 'autocomplete-accept', - name: 'Autocomplete accept', + name: t('main.autocompleteAccept'), editorCheckCallback: ( checking: boolean, editor: Editor, @@ -265,7 +265,7 @@ export default class InfioPlugin extends Plugin { this.addCommand({ id: 'autocomplete-predict', - name: 'Autocomplete predict', + name: t('main.autocompletePredict'), editorCheckCallback: ( checking: boolean, editor: Editor, @@ -288,7 +288,7 @@ export default class InfioPlugin extends Plugin { this.addCommand({ id: "autocomplete-toggle", - name: "Autocomplete toggle", + name: t('main.autocompleteToggle'), callback: () => { const newValue = !this.settings.autocompleteEnabled; this.setSettings({ @@ -300,7 +300,7 @@ export default class InfioPlugin extends Plugin { this.addCommand({ id: "autocomplete-enable", - name: "Autocomplete enable", + name: t('main.autocompleteEnable'), checkCallback: (checking) => { if (checking) { return !this.settings.autocompleteEnabled; @@ -316,7 +316,7 @@ export default class InfioPlugin extends Plugin { this.addCommand({ id: "autocomplete-disable", - name: "Autocomplete disable", + name: t('main.autocompleteDisable'), checkCallback: (checking) => { if (checking) { return this.settings.autocompleteEnabled; @@ -332,7 +332,7 @@ export default class InfioPlugin extends Plugin { this.addCommand({ id: "ai-inline-edit", - name: "Inline edit", + name: t('main.inlineEditCommand'), // hotkeys: [ // { // modifiers: ['Mod', 'Shift'], @@ -342,7 +342,7 @@ export default class InfioPlugin extends Plugin { editorCallback: (editor: Editor) => { const selection = editor.getSelection(); if (!selection) { - new Notice("Please select some text first"); + new Notice(t('notifications.selectTextFirst')); return; } // Get the selection start position @@ -495,7 +495,7 @@ export default class InfioPlugin extends Plugin { } catch (error) { console.error('Failed to migrate to JSON storage:', error) new Notice( - 'Failed to migrate to JSON storage. Please check the console for details.', + t('notifications.migrationFailed'), ) } } @@ -505,7 +505,7 @@ export default class InfioPlugin extends Plugin { if (leaves.length === 0 || !(leaves[0].view instanceof ChatView)) { return } - new Notice('Reloading "infio" due to migration', 1000) + new Notice(t('notifications.reloadingInfio'), 1000) leaves[0].detach() await this.activateChatView() }