add chat view & edit line local lang
This commit is contained in:
parent
dc4ce4aeca
commit
2f824134b6
@ -2,6 +2,7 @@ import * as Tooltip from '@radix-ui/react-tooltip'
|
||||
import { Check, CopyIcon } from 'lucide-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import { t } from '../../lang/helpers'
|
||||
import { ChatAssistantMessage } from '../../types/chat'
|
||||
import { calculateLLMCost } from '../../utils/price-calculator'
|
||||
|
||||
@ -35,7 +36,7 @@ function CopyButton({ message }: { message: ChatAssistantMessage }) {
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="infio-tooltip-content">
|
||||
Copy message
|
||||
{t('chat.reactMarkdown.copyMsg')}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
@ -76,7 +77,7 @@ function LLMResponesInfoButton({ message }: { message: ChatAssistantMessage }) {
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="infio-tooltip-content">
|
||||
View details
|
||||
{t('chat.reactMarkdown.viewDetails')}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
|
||||
@ -2,8 +2,10 @@ import * as Popover from '@radix-ui/react-popover'
|
||||
import { Pencil, Trash2 } from 'lucide-react'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { t } from '../../lang/helpers'
|
||||
import { ChatConversationMeta } from '../../types/chat'
|
||||
|
||||
|
||||
function TitleInput({
|
||||
title,
|
||||
onSubmit,
|
||||
@ -165,7 +167,7 @@ export function ChatHistory({
|
||||
<ul>
|
||||
{chatList.length === 0 ? (
|
||||
<li className="infio-chat-list-dropdown-empty">
|
||||
No conversations
|
||||
{t('chat.history.noConversations')}
|
||||
</li>
|
||||
) : (
|
||||
chatList.map((chat, index) => (
|
||||
|
||||
@ -31,6 +31,7 @@ import {
|
||||
import { regexSearchFiles } from '../../core/ripgrep'
|
||||
import { useChatHistory } from '../../hooks/use-chat-history'
|
||||
import { useCustomModes } from '../../hooks/use-custom-mode'
|
||||
import { t } from '../../lang/helpers'
|
||||
import { ApplyStatus, ToolArgs } from '../../types/apply'
|
||||
import { ChatMessage, ChatUserMessage } from '../../types/chat'
|
||||
import {
|
||||
@ -165,7 +166,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
||||
}
|
||||
}
|
||||
|
||||
const [tab, setTab] = useState<'chat' | 'commands' | 'custom-mode'>('custom-mode')
|
||||
const [tab, setTab] = useState<'chat' | 'commands' | 'custom-mode'>('chat')
|
||||
const [selectedSerializedNodes, setSelectedSerializedNodes] = useState<BaseSerializedNode[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
@ -216,7 +217,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
||||
abortActiveStreams()
|
||||
const conversation = await getChatMessagesById(conversationId)
|
||||
if (!conversation) {
|
||||
throw new Error('Conversation not found')
|
||||
throw new Error(t('chat.errors.conversationNotFound'))
|
||||
}
|
||||
setCurrentConversationId(conversationId)
|
||||
setChatMessages(conversation)
|
||||
@ -227,8 +228,8 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
||||
type: 'idle',
|
||||
})
|
||||
} catch (error) {
|
||||
new Notice('Failed to load conversation')
|
||||
console.error('Failed to load conversation', error)
|
||||
new Notice(t('chat.errors.failedToLoadConversation'))
|
||||
console.error(t('chat.errors.failedToLoadConversation'), error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1031,7 +1032,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
||||
{submitMutation.isPending && (
|
||||
<button onClick={abortActiveStreams} className="infio-stop-gen-btn">
|
||||
<CircleStop size={16} />
|
||||
<div>Stop generation</div>
|
||||
<div>{t('chat.stop')}</div>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -8,6 +8,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { TemplateContent } from '../../database/schema'
|
||||
import { useCommands } from '../../hooks/use-commands'
|
||||
import { t } from '../../lang/helpers'
|
||||
|
||||
import LexicalContentEditable from './chat-input/LexicalContentEditable'
|
||||
|
||||
@ -107,11 +108,11 @@ const CommandsView = (
|
||||
const serializedEditorState = editorRef.current.toJSON()
|
||||
const nodes = serializedEditorState.editorState.root.children
|
||||
if (nodes.length === 0) {
|
||||
new Notice('Please enter a content for your template')
|
||||
new Notice(String(t('command.errorContentRequired')))
|
||||
return
|
||||
}
|
||||
if (newCommandName.trim().length === 0) {
|
||||
new Notice('Please enter a name for your template')
|
||||
new Notice(String(t('command.errorNameRequired')))
|
||||
return
|
||||
}
|
||||
|
||||
@ -140,13 +141,13 @@ const CommandsView = (
|
||||
const nameInput = nameInputRefs.current.get(id)
|
||||
const currContentEditorRef = contentEditorRefs.current.get(id)
|
||||
if (!currContentEditorRef) {
|
||||
new Notice('Please enter a content for your template')
|
||||
new Notice(String(t('command.errorContentRequired')))
|
||||
return
|
||||
}
|
||||
const serializedEditorState = currContentEditorRef.toJSON()
|
||||
const nodes = serializedEditorState.editorState.root.children
|
||||
if (nodes.length === 0) {
|
||||
new Notice('Please enter a content for your template')
|
||||
new Notice(String(t('command.errorContentRequired')))
|
||||
return
|
||||
}
|
||||
await updateCommand(
|
||||
@ -190,15 +191,15 @@ const CommandsView = (
|
||||
{/* header */}
|
||||
<div className="infio-commands-header">
|
||||
<div className="infio-commands-new">
|
||||
<h2 className="infio-commands-header-title">Create Quick Command</h2>
|
||||
<div className="infio-commands-label">Name</div>
|
||||
<h2 className="infio-commands-header-title">{t('command.createQuickCommand')}</h2>
|
||||
<div className="infio-commands-label">{t('command.name')}</div>
|
||||
<input
|
||||
type="text"
|
||||
value={newCommandName}
|
||||
onChange={(e) => setNewCommandName(e.target.value)}
|
||||
className="infio-commands-input"
|
||||
/>
|
||||
<div className="infio-commands-label">Content</div>
|
||||
<div className="infio-commands-label">{t('command.content')}</div>
|
||||
<div className="infio-commands-textarea">
|
||||
<LexicalContentEditable
|
||||
initialEditorState={initialEditorState}
|
||||
@ -211,7 +212,7 @@ const CommandsView = (
|
||||
className="infio-commands-add-btn"
|
||||
disabled={!newCommandName.trim()}
|
||||
>
|
||||
<span>Create Command</span>
|
||||
<span>{t('command.createCommand')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -221,7 +222,7 @@ const CommandsView = (
|
||||
<Search size={18} className="infio-commands-search-icon" />
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search Command..."
|
||||
placeholder={t('command.searchPlaceholder')}
|
||||
value={searchTerm}
|
||||
onChange={handleSearch}
|
||||
className="infio-commands-search-input"
|
||||
@ -232,7 +233,7 @@ const CommandsView = (
|
||||
<div className="infio-commands-list">
|
||||
{filteredCommands.length === 0 ? (
|
||||
<div className="infio-commands-empty">
|
||||
<p>No commands found</p>
|
||||
<p>{t('command.noCommandsFound')}</p>
|
||||
</div>
|
||||
) : (
|
||||
filteredCommands.map(command => (
|
||||
@ -260,7 +261,7 @@ const CommandsView = (
|
||||
onClick={() => handleSaveEdit(command.id)}
|
||||
className="infio-commands-add-btn"
|
||||
>
|
||||
<span>Update Command</span>
|
||||
<span>{t('command.updateCommand')}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -9,13 +9,12 @@ import { useRAG } from '../../contexts/RAGContext';
|
||||
import { useSettings } from '../../contexts/SettingsContext';
|
||||
import { CustomMode, GroupEntry, ToolGroup } from '../../database/json/custom-mode/types';
|
||||
import { useCustomModes } from '../../hooks/use-custom-mode';
|
||||
import { t } from '../../lang/helpers';
|
||||
import { PreviewView, PreviewViewState } from '../../PreviewView';
|
||||
import { modes as buildinModes } from '../../utils/modes';
|
||||
import { openOrCreateMarkdownFile } from '../../utils/obsidian';
|
||||
import { PromptGenerator, getFullLanguageName } from '../../utils/prompt-generator';
|
||||
|
||||
import { t } from '../../lang/helpers';
|
||||
|
||||
const CustomModeView = () => {
|
||||
const app = useApp()
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
Info,
|
||||
} from 'lucide-react'
|
||||
|
||||
import { t } from '../../lang/helpers'
|
||||
import { ResponseUsage } from '../../types/llm/response'
|
||||
|
||||
type LLMResponseInfoProps = {
|
||||
@ -30,27 +31,27 @@ export default function LLMResponseInfoPopover({
|
||||
</Popover.Trigger>
|
||||
{usage ? (
|
||||
<Popover.Content className="infio-chat-popover-content infio-llm-info-content">
|
||||
<div className="infio-llm-info-header">LLM response information</div>
|
||||
<div className="infio-llm-info-header">{t('chat.LLMResponseInfoPopover.header')}</div>
|
||||
<div className="infio-llm-info-tokens">
|
||||
<div className="infio-llm-info-tokens-header">Token count</div>
|
||||
<div className="infio-llm-info-tokens-header">{t('chat.LLMResponseInfoPopover.tokenCount')}</div>
|
||||
<div className="infio-llm-info-tokens-grid">
|
||||
<div className="infio-llm-info-token-row">
|
||||
<ArrowUp className="infio-llm-info-icon--input" />
|
||||
<span>Input:</span>
|
||||
<span>{t('chat.LLMResponseInfoPopover.promptTokens')}</span>
|
||||
<span className="infio-llm-info-token-value">
|
||||
{usage.prompt_tokens}
|
||||
</span>
|
||||
</div>
|
||||
<div className="infio-llm-info-token-row">
|
||||
<ArrowDown className="infio-llm-info-icon--output" />
|
||||
<span>Output:</span>
|
||||
<span>{t('chat.LLMResponseInfoPopover.completionTokens')}</span>
|
||||
<span className="infio-llm-info-token-value">
|
||||
{usage.completion_tokens}
|
||||
</span>
|
||||
</div>
|
||||
<div className="infio-llm-info-token-row infio-llm-info-token-total">
|
||||
<ArrowRightLeft className="infio-llm-info-icon--total" />
|
||||
<span>Total:</span>
|
||||
<span>{t('chat.LLMResponseInfoPopover.totalTokens')}</span>
|
||||
<span className="infio-llm-info-token-value">
|
||||
{usage.total_tokens}
|
||||
</span>
|
||||
@ -59,24 +60,24 @@ export default function LLMResponseInfoPopover({
|
||||
</div>
|
||||
<div className="infio-llm-info-footer-row">
|
||||
<Coins className="infio-llm-info-icon--footer" />
|
||||
<span>Estimated price:</span>
|
||||
<span>{t('chat.LLMResponseInfoPopover.estimatedPrice')}</span>
|
||||
<span className="infio-llm-info-footer-value">
|
||||
{estimatedPrice === null
|
||||
? 'Not available'
|
||||
? t('chat.LLMResponseInfoPopover.notAvailable')
|
||||
: `$${estimatedPrice.toFixed(4)}`}
|
||||
</span>
|
||||
</div>
|
||||
<div className="infio-llm-info-footer-row">
|
||||
<Cpu className="infio-llm-info-icon--footer" />
|
||||
<span>Model:</span>
|
||||
<span>{t('chat.LLMResponseInfoPopover.model')}</span>
|
||||
<span className="infio-llm-info-footer-value infio-llm-info-model">
|
||||
{model ?? 'Not available'}
|
||||
{model ?? t('chat.LLMResponseInfoPopover.notAvailable')}
|
||||
</span>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
) : (
|
||||
<Popover.Content className="infio-chat-popover-content">
|
||||
<div>Usage statistics are not available for this model</div>
|
||||
<div>{t('chat.LLMResponseInfoPopover.usageNotAvailable')}</div>
|
||||
</Popover.Content>
|
||||
)}
|
||||
</Popover.Root>
|
||||
|
||||
@ -2,6 +2,7 @@ import { Check, Diff, Loader2, X } from 'lucide-react'
|
||||
import { PropsWithChildren, useState } from 'react'
|
||||
|
||||
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
||||
|
||||
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
||||
@ -55,23 +56,23 @@ export default function MarkdownApplyDiffBlock({
|
||||
{
|
||||
!finish ? (
|
||||
<>
|
||||
<Loader2 className="spinner" size={14} /> Loading...
|
||||
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.loading')}
|
||||
</>
|
||||
) : applyStatus === ApplyStatus.Idle ? (
|
||||
applying ? (
|
||||
<>
|
||||
<Loader2 className="spinner" size={14} /> Applying...
|
||||
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.applying')}
|
||||
</>
|
||||
) : (
|
||||
'Apply'
|
||||
t('chat.reactMarkdown.apply')
|
||||
)
|
||||
) : applyStatus === ApplyStatus.Applied ? (
|
||||
<>
|
||||
<Check size={14} /> Success
|
||||
<Check size={14} /> {t('chat.reactMarkdown.success')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<X size={14} /> Failed
|
||||
<X size={14} /> {t('chat.reactMarkdown.failed')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@ -2,6 +2,7 @@ import { Check, CopyIcon, Edit, Loader2, X } from 'lucide-react'
|
||||
import { PropsWithChildren, useMemo, useState } from 'react'
|
||||
|
||||
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
||||
|
||||
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
||||
@ -63,7 +64,7 @@ export default function MarkdownEditFileBlock({
|
||||
{path && (
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<Edit size={10} className="infio-chat-code-block-header-icon" />
|
||||
{mode}: {path}
|
||||
{t('chat.reactMarkdown.editOrApplyDiff').replace('{mode}', mode).replace('{path}', path)}
|
||||
</div>
|
||||
)}
|
||||
<div className={'infio-chat-code-block-header-button'}>
|
||||
@ -74,34 +75,34 @@ export default function MarkdownEditFileBlock({
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<Check size={10} /> Copied
|
||||
<Check size={10} /> {t('chat.reactMarkdown.copied')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<CopyIcon size={10} /> Copy
|
||||
<CopyIcon size={10} /> {t('chat.reactMarkdown.copy')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleApply}
|
||||
style={{ color: '#008000' }}
|
||||
className="infio-apply-button"
|
||||
disabled={applyStatus !== ApplyStatus.Idle || applying}
|
||||
>
|
||||
{applyStatus === ApplyStatus.Idle ? (
|
||||
applying ? (
|
||||
<>
|
||||
<Loader2 className="spinner" size={14} /> Applying...
|
||||
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.applying')}
|
||||
</>
|
||||
) : (
|
||||
'Apply'
|
||||
t('chat.reactMarkdown.apply')
|
||||
)
|
||||
) : applyStatus === ApplyStatus.Applied ? (
|
||||
<>
|
||||
<Check size={14} /> Success
|
||||
<Check size={14} /> {t('chat.reactMarkdown.success')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<X size={14} /> Failed
|
||||
<X size={14} /> {t('chat.reactMarkdown.failed')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Check, ChevronDown, ChevronRight, Globe, Loader2, X } from 'lucide-react'
|
||||
import React, { useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, FetchUrlsContentToolArgs } from "../../../types/apply"
|
||||
|
||||
export default function MarkdownFetchUrlsContentBlock({
|
||||
@ -38,7 +39,7 @@ export default function MarkdownFetchUrlsContentBlock({
|
||||
<div className="infio-chat-code-block-header">
|
||||
<div className="infio-chat-code-block-header-filename">
|
||||
<Globe size={10} className="infio-chat-code-block-header-icon" />
|
||||
Fetch URLs Content
|
||||
{t('chat.reactMarkdown.fetchUrlsContent')}
|
||||
</div>
|
||||
<div className="infio-chat-code-block-header-button">
|
||||
<button
|
||||
@ -48,15 +49,15 @@ export default function MarkdownFetchUrlsContentBlock({
|
||||
{
|
||||
!finish || applyStatus === ApplyStatus.Idle ? (
|
||||
<>
|
||||
<Loader2 className="spinner" size={14} /> Fetching...
|
||||
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.fetching')}
|
||||
</>
|
||||
) : applyStatus === ApplyStatus.Applied ? (
|
||||
<>
|
||||
<Check size={14} /> Done
|
||||
<Check size={14} /> {t('chat.reactMarkdown.done')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<X size={14} /> Failed
|
||||
<X size={14} /> {t('chat.reactMarkdown.failed')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@ -2,6 +2,7 @@ import { FolderOpen } from 'lucide-react'
|
||||
import React from 'react'
|
||||
|
||||
import { useApp } from "../../../contexts/AppContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, ListFilesToolArgs } from "../../../types/apply"
|
||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
||||
|
||||
@ -42,7 +43,7 @@ export default function MarkdownListFilesBlock({
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<FolderOpen size={14} className="infio-chat-code-block-header-icon" />
|
||||
List files: {path}
|
||||
{t('chat.reactMarkdown.listFiles').replace('{path}', path)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,6 +2,7 @@ import { ExternalLink } from 'lucide-react'
|
||||
import React from 'react'
|
||||
|
||||
import { useApp } from "../../../contexts/AppContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, ReadFileToolArgs } from "../../../types/apply"
|
||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
||||
|
||||
@ -39,7 +40,7 @@ export default function MarkdownReadFileBlock({
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<ExternalLink size={10} className="infio-chat-code-block-header-icon" />
|
||||
Read file: {path}
|
||||
{t('chat.reactMarkdown.readFile').replace('{path}', path)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,6 +2,7 @@ import { ChevronDown, ChevronRight, Brain } from 'lucide-react'
|
||||
import { PropsWithChildren, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
|
||||
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
||||
|
||||
@ -28,7 +29,7 @@ export default function MarkdownReasoningBlock({
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<Brain size={10} className="infio-chat-code-block-header-icon" />
|
||||
Reasoning
|
||||
{t('chat.reactMarkdown.reasoning')}
|
||||
</div>
|
||||
<button
|
||||
className="clickable-icon infio-chat-list-dropdown"
|
||||
|
||||
@ -2,6 +2,7 @@ import { FileSearch } from 'lucide-react'
|
||||
import React from 'react'
|
||||
|
||||
import { useApp } from "../../../contexts/AppContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, RegexSearchFilesToolArgs } from "../../../types/apply"
|
||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
||||
|
||||
@ -43,7 +44,7 @@ export default function MarkdownRegexSearchFilesBlock({
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<FileSearch size={14} className="infio-chat-code-block-header-icon" />
|
||||
<span>regex search files "{regex}" in {path}</span>
|
||||
<span>{t('chat.reactMarkdown.regexSearchInPath').replace('{regex}', regex).replace('{path}', path)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -3,6 +3,7 @@ import React from 'react'
|
||||
|
||||
import { useApp } from '../../../contexts/AppContext'
|
||||
import { useDarkModeContext } from '../../../contexts/DarkModeContext'
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, SearchAndReplaceToolArgs } from '../../../types/apply'
|
||||
import { openMarkdownFile } from '../../../utils/obsidian'
|
||||
|
||||
@ -52,7 +53,7 @@ export default function MarkdownSearchAndReplace({
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<Replace size={10} className="infio-chat-code-block-header-icon" />
|
||||
Search and replace in {path}
|
||||
{t('chat.reactMarkdown.searchAndReplaceInPath').replace('{path}', path)}
|
||||
</div>
|
||||
<div className={'infio-chat-code-block-header-button'}>
|
||||
<button
|
||||
@ -66,18 +67,18 @@ export default function MarkdownSearchAndReplace({
|
||||
) : applyStatus === ApplyStatus.Idle ? (
|
||||
applying ? (
|
||||
<>
|
||||
<Loader2 className="spinner" size={14} /> Applying...
|
||||
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.applying')}
|
||||
</>
|
||||
) : (
|
||||
'Apply'
|
||||
t('chat.reactMarkdown.apply')
|
||||
)
|
||||
) : applyStatus === ApplyStatus.Applied ? (
|
||||
<>
|
||||
<Check size={14} /> Success
|
||||
<Check size={14} /> {t('chat.reactMarkdown.success')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<X size={14} /> Failed
|
||||
<X size={14} /> {t('chat.reactMarkdown.failed')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@ -2,6 +2,7 @@ import { Check, Loader2, Search, X } from 'lucide-react'
|
||||
import React from 'react'
|
||||
|
||||
import { useSettings } from "../../../contexts/SettingsContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, SearchWebToolArgs } from "../../../types/apply"
|
||||
|
||||
export default function MarkdownWebSearchBlock({
|
||||
@ -46,7 +47,7 @@ export default function MarkdownWebSearchBlock({
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<Search size={14} className="infio-chat-code-block-header-icon" />
|
||||
Web search: {query}
|
||||
{t('chat.reactMarkdown.webSearch').replace('{query}', query)}
|
||||
</div>
|
||||
<div className={'infio-chat-code-block-header-button'}>
|
||||
<button
|
||||
@ -56,15 +57,15 @@ export default function MarkdownWebSearchBlock({
|
||||
{
|
||||
!finish || applyStatus === ApplyStatus.Idle ? (
|
||||
<>
|
||||
<Loader2 className="spinner" size={14} /> Searching...
|
||||
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.searching')}
|
||||
</>
|
||||
) : applyStatus === ApplyStatus.Applied ? (
|
||||
<>
|
||||
<Check size={14} /> Done
|
||||
<Check size={14} /> {t('chat.reactMarkdown.done')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<X size={14} /> Failed
|
||||
<X size={14} /> {t('chat.reactMarkdown.failed')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@ -2,6 +2,7 @@ import { FileSearch } from 'lucide-react'
|
||||
import React from 'react'
|
||||
|
||||
import { useApp } from "../../../contexts/AppContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, SemanticSearchFilesToolArgs } from "../../../types/apply"
|
||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
||||
|
||||
@ -42,7 +43,7 @@ export default function MarkdownSemanticSearchFilesBlock({
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<FileSearch size={14} className="infio-chat-code-block-header-icon" />
|
||||
<span>semantic search files "{query}" in {path}</span>
|
||||
<span>{t('chat.reactMarkdown.semanticSearchInPath').replace('{query}', query).replace('{path}', path)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,6 +2,7 @@ import { Check, Loader2, Settings2, X } from 'lucide-react'
|
||||
import { PropsWithChildren, useState } from 'react'
|
||||
|
||||
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
||||
|
||||
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
||||
@ -40,7 +41,7 @@ export default function MarkdownSwitchModeBlock({
|
||||
<div className={'infio-chat-code-block-header'}>
|
||||
<div className={'infio-chat-code-block-header-filename'}>
|
||||
<Settings2 size={10} className="infio-chat-code-block-header-icon" />
|
||||
Switch to "{mode.charAt(0).toUpperCase() + mode.slice(1)}" mode
|
||||
{t('chat.reactMarkdown.switchToMode').replace('{mode}', mode.charAt(0).toUpperCase() + mode.slice(1))}
|
||||
</div>
|
||||
<div className={'infio-chat-code-block-header-button'}>
|
||||
<button
|
||||
@ -51,18 +52,18 @@ export default function MarkdownSwitchModeBlock({
|
||||
{applyStatus === ApplyStatus.Idle ? (
|
||||
applying ? (
|
||||
<>
|
||||
<Loader2 className="spinner" size={14} /> Allowing...
|
||||
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.allowing')}
|
||||
</>
|
||||
) : (
|
||||
'Allow'
|
||||
t('chat.reactMarkdown.allow')
|
||||
)
|
||||
) : applyStatus === ApplyStatus.Applied ? (
|
||||
<>
|
||||
<Check size={14} /> Success
|
||||
<Check size={14} /> {t('chat.reactMarkdown.success')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<X size={14} /> Failed
|
||||
<X size={14} /> {t('chat.reactMarkdown.failed')}
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
|
||||
@ -5,6 +5,8 @@ import ReactMarkdown from 'react-markdown';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import { useApp } from 'src/contexts/AppContext';
|
||||
|
||||
import { t } from '../../../lang/helpers'
|
||||
|
||||
function CopyButton({ message }: { message: string }) {
|
||||
const [copied, setCopied] = useState(false)
|
||||
|
||||
@ -33,7 +35,7 @@ function CopyButton({ message }: { message: string }) {
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="infio-tooltip-content">
|
||||
Copy message
|
||||
{t('chat.reactMarkdown.copyMsg')}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
@ -77,7 +79,7 @@ function CreateNewFileButton({ message }: { message: string }) {
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Portal>
|
||||
<Tooltip.Content className="infio-tooltip-content">
|
||||
Create new note
|
||||
{t('chat.reactMarkdown.createNewNote')}
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Portal>
|
||||
</Tooltip.Root>
|
||||
@ -123,9 +125,9 @@ const MarkdownWithIcons = ({
|
||||
|
||||
switch (iconName) {
|
||||
case 'ask_followup_question':
|
||||
return 'Ask Followup Question:';
|
||||
return t('chat.reactMarkdown.askFollowupQuestion');
|
||||
case 'attempt_completion':
|
||||
return 'Task Completion';
|
||||
return t('chat.reactMarkdown.taskCompletion');
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { SelectVector } from '../../database/schema'
|
||||
|
||||
import { t } from '../../lang/helpers'
|
||||
export type QueryProgressState =
|
||||
| {
|
||||
type: 'reading-mentionables'
|
||||
@ -38,7 +38,7 @@ export default function QueryProgress({
|
||||
return (
|
||||
<div className="infio-query-progress">
|
||||
<p>
|
||||
Reading mentioned files
|
||||
{t('chat.queryProgress.readingMentionableFiles')}
|
||||
<DotLoader />
|
||||
</p>
|
||||
</div>
|
||||
@ -47,17 +47,17 @@ export default function QueryProgress({
|
||||
return (
|
||||
<div className="infio-query-progress">
|
||||
<p>
|
||||
{`Indexing ${state.indexProgress.totalFiles} file`}
|
||||
{`${t('chat.queryProgress.indexing')} ${state.indexProgress.totalFiles} ${t('chat.queryProgress.file')}`}
|
||||
<DotLoader />
|
||||
</p>
|
||||
<p className="infio-query-progress-detail">{`${state.indexProgress.completedChunks}/${state.indexProgress.totalChunks} chunks indexed`}</p>
|
||||
<p className="infio-query-progress-detail">{`${state.indexProgress.completedChunks}/${state.indexProgress.totalChunks} ${t('chat.queryProgress.chunkIndexed')}`}</p>
|
||||
</div>
|
||||
)
|
||||
case 'querying':
|
||||
return (
|
||||
<div className="infio-query-progress">
|
||||
<p>
|
||||
Querying the vault
|
||||
{t('chat.queryProgress.queryingVault')}
|
||||
<DotLoader />
|
||||
</p>
|
||||
</div>
|
||||
@ -66,7 +66,7 @@ export default function QueryProgress({
|
||||
return (
|
||||
<div className="infio-query-progress">
|
||||
<p>
|
||||
Reading related files
|
||||
{t('chat.queryProgress.readingRelatedFiles')}
|
||||
<DotLoader />
|
||||
</p>
|
||||
{state.queryResult.map((result) => (
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
import { Platform } from 'obsidian';
|
||||
import React from 'react';
|
||||
|
||||
import { t } from '../../lang/helpers'
|
||||
|
||||
const ShortcutInfo: React.FC = () => {
|
||||
const modKey = Platform.isMacOS ? 'Cmd' : 'Ctrl';
|
||||
|
||||
const shortcuts = [
|
||||
{
|
||||
label: 'Edit inline',
|
||||
label: t('chat.shortcutInfo.editInline'),
|
||||
shortcut: `${modKey}+Shift+K`,
|
||||
},
|
||||
{
|
||||
label: 'Chat with select',
|
||||
label: t('chat.shortcutInfo.chatWithSelect'),
|
||||
shortcut: `${modKey}+Shift+L`,
|
||||
},
|
||||
{
|
||||
label: 'Submit with vault',
|
||||
label: t('chat.shortcutInfo.submitWithVault'),
|
||||
shortcut: `${modKey}+Shift+Enter`,
|
||||
}
|
||||
];
|
||||
|
||||
@ -5,67 +5,69 @@ import { useState } from 'react'
|
||||
|
||||
import { useApp } from '../../contexts/AppContext'
|
||||
import { SelectVector } from '../../database/schema'
|
||||
import { t } from '../../lang/helpers'
|
||||
import { openMarkdownFile } from '../../utils/obsidian'
|
||||
|
||||
function SimiliartySearchItem({
|
||||
chunk,
|
||||
chunk,
|
||||
}: {
|
||||
chunk: Omit<SelectVector, 'embedding'> & {
|
||||
similarity: number
|
||||
}
|
||||
chunk: Omit<SelectVector, 'embedding'> & {
|
||||
similarity: number
|
||||
}
|
||||
}) {
|
||||
const app = useApp()
|
||||
const app = useApp()
|
||||
|
||||
const handleClick = () => {
|
||||
openMarkdownFile(app, chunk.path, chunk.metadata.startLine)
|
||||
}
|
||||
return (
|
||||
<div onClick={handleClick} className="infio-similarity-search-item">
|
||||
<div className="infio-similarity-search-item__similarity">
|
||||
{chunk.similarity.toFixed(3)}
|
||||
</div>
|
||||
<div className="infio-similarity-search-item__path">
|
||||
{path.basename(chunk.path)}
|
||||
</div>
|
||||
<div className="infio-similarity-search-item__line-numbers">
|
||||
{`${chunk.metadata.startLine} - ${chunk.metadata.endLine}`}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
const handleClick = () => {
|
||||
openMarkdownFile(app, chunk.path, chunk.metadata.startLine)
|
||||
}
|
||||
return (
|
||||
<div onClick={handleClick} className="infio-similarity-search-item">
|
||||
<div className="infio-similarity-search-item__similarity">
|
||||
{chunk.similarity.toFixed(3)}
|
||||
</div>
|
||||
<div className="infio-similarity-search-item__path">
|
||||
{path.basename(chunk.path)}
|
||||
</div>
|
||||
<div className="infio-similarity-search-item__line-numbers">
|
||||
{`${chunk.metadata.startLine} - ${chunk.metadata.endLine}`}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function SimilaritySearchResults({
|
||||
similaritySearchResults,
|
||||
similaritySearchResults,
|
||||
}: {
|
||||
similaritySearchResults: (Omit<SelectVector, 'embedding'> & {
|
||||
similarity: number
|
||||
})[]
|
||||
similaritySearchResults: (Omit<SelectVector, 'embedding'> & {
|
||||
similarity: number
|
||||
})[]
|
||||
}) {
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="infio-similarity-search-results">
|
||||
<div
|
||||
onClick={() => {
|
||||
setIsOpen(!isOpen)
|
||||
}}
|
||||
className="infio-similarity-search-results__trigger"
|
||||
>
|
||||
{isOpen ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
|
||||
<div>Show Referenced Documents ({similaritySearchResults.length})</div>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
{similaritySearchResults.map((chunk) => (
|
||||
<SimiliartySearchItem key={chunk.id} chunk={chunk} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className="infio-similarity-search-results">
|
||||
<div
|
||||
onClick={() => {
|
||||
setIsOpen(!isOpen)
|
||||
}}
|
||||
className="infio-similarity-search-results__trigger"
|
||||
>
|
||||
{isOpen ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
|
||||
<div>
|
||||
{t('chat.searchResults.showReferencedDocuments')} ({similaritySearchResults.length})</div>
|
||||
</div>
|
||||
{isOpen && (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
{similaritySearchResults.map((chunk) => (
|
||||
<SimiliartySearchItem key={chunk.id} chunk={chunk} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { ImageIcon } from 'lucide-react'
|
||||
import { TFile } from 'obsidian'
|
||||
|
||||
import { useApp } from '../../../contexts/AppContext'
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ImageSelectorModal } from '../../modals/ImageSelectorModal'
|
||||
|
||||
export function ImageUploadButton({
|
||||
@ -34,7 +35,7 @@ export function ImageUploadButton({
|
||||
<div className="infio-chat-user-input-submit-button-icons">
|
||||
<ImageIcon size={12} />
|
||||
</div>
|
||||
<div>Image</div>
|
||||
<div>{t('chat.input.image')}</div>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import { ChevronDown, ChevronUp, Star, StarOff } from 'lucide-react'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { useSettings } from '../../../contexts/SettingsContext'
|
||||
import { t } from '../../../lang/helpers'
|
||||
import { ApiProvider } from '../../../types/llm/model'
|
||||
import { GetAllProviders, GetProviderModelIds } from "../../../utils/api"
|
||||
|
||||
@ -256,7 +257,7 @@ export function ModelSelect() {
|
||||
{settings.collectedChatModels?.length > 0 && (
|
||||
<div className="infio-model-section">
|
||||
<div className="infio-model-section-title">
|
||||
<Star size={12} className="infio-star-active" /> collected models
|
||||
<Star size={12} className="infio-star-active" /> {t('chat.input.collectedModels')}
|
||||
</div>
|
||||
<ul className="infio-collected-models-list">
|
||||
{settings.collectedChatModels.map((collectedModel, index) => (
|
||||
@ -415,7 +416,7 @@ export function ModelSelect() {
|
||||
)}
|
||||
</div>
|
||||
{isLoading ? (
|
||||
<div className="infio-loading">loading...</div>
|
||||
<div className="infio-loading">{t('chat.input.loading')}</div>
|
||||
) : (
|
||||
<div className="infio-model-section">
|
||||
<ul>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { CornerDownLeftIcon } from 'lucide-react'
|
||||
|
||||
import { t } from '../../../lang/helpers'
|
||||
|
||||
export function SubmitButton({ onClick }: { onClick: () => void }) {
|
||||
return (
|
||||
<button className="infio-chat-user-input-submit-button" onClick={onClick}>
|
||||
<div>submit</div>
|
||||
{t('chat.input.submit')}
|
||||
<div className="infio-chat-user-input-submit-button-icons">
|
||||
<CornerDownLeftIcon size={12} />
|
||||
</div>
|
||||
|
||||
@ -8,6 +8,8 @@ import {
|
||||
} from 'lexical'
|
||||
import { CSSProperties, useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
import { t } from '../../../../../lang/helpers'
|
||||
|
||||
export default function CreateCommandPopoverPlugin({
|
||||
anchorElement,
|
||||
contentEditableElement,
|
||||
@ -121,7 +123,7 @@ export default function CreateCommandPopoverPlugin({
|
||||
setIsPopoverOpen(false)
|
||||
}}
|
||||
>
|
||||
create command
|
||||
{t('chat.input.createCommand')}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { APPLY_VIEW_TYPE } from '../../constants';
|
||||
import LLMManager from '../../core/llm/manager';
|
||||
import { t } from '../../lang/helpers';
|
||||
import { InfioSettings } from '../../types/settings';
|
||||
import { GetProviderModelIds } from '../../utils/api';
|
||||
import { ApplyEditToFile } from '../../utils/apply';
|
||||
@ -52,7 +53,7 @@ const InputArea: React.FC<InputAreaProps> = ({ value, onChange, handleSubmit, ha
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
className="infio-ai-block-content"
|
||||
placeholder="Input instruction, Enter to submit, Esc to close"
|
||||
placeholder={t('inlineEdit.placeholder')}
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
@ -83,8 +84,9 @@ const ControlArea: React.FC<ControlAreaProps> = ({
|
||||
try {
|
||||
const models = await GetProviderModelIds(settings.chatModelProvider);
|
||||
setProviderModels(models);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch provider models:", error);
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
console.error(t("inlineEdit.fetchModelsError"), error.message);
|
||||
}
|
||||
};
|
||||
fetchModels();
|
||||
@ -109,9 +111,9 @@ const ControlArea: React.FC<ControlAreaProps> = ({
|
||||
onClick={onSubmit}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
{isSubmitting ? "submitting..." : (
|
||||
{isSubmitting ? t("inlineEdit.submitting") : (
|
||||
<>
|
||||
submit
|
||||
{t("inlineEdit.submit")}
|
||||
<CornerDownLeft size={11} className="infio-ai-block-submit-icon" />
|
||||
</>
|
||||
)}
|
||||
@ -134,7 +136,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
||||
|
||||
const promptGenerator = new PromptGenerator(
|
||||
async () => {
|
||||
throw new Error("RAG not needed for inline edit");
|
||||
throw new Error(t("inlineEdit.ragNotNeeded"));
|
||||
},
|
||||
plugin.app,
|
||||
settings
|
||||
@ -153,19 +155,19 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
||||
const getActiveContext = async () => {
|
||||
const activeFile = plugin.app.workspace.getActiveFile();
|
||||
if (!activeFile) {
|
||||
console.error("No active file");
|
||||
console.error(t("inlineEdit.noActiveFile"));
|
||||
return {};
|
||||
}
|
||||
|
||||
const editor = plugin.app.workspace.getActiveViewOfType(MarkdownView)?.editor;
|
||||
if (!editor) {
|
||||
console.error("No active editor");
|
||||
console.error(t("inlineEdit.noActiveEditor"));
|
||||
return { activeFile };
|
||||
}
|
||||
|
||||
const selection = editor.getSelection();
|
||||
if (!selection) {
|
||||
console.error("No text selected");
|
||||
console.error(t("inlineEdit.noTextSelected"));
|
||||
return { activeFile, editor };
|
||||
}
|
||||
|
||||
@ -190,7 +192,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
||||
try {
|
||||
const { activeFile, editor, selection } = await getActiveContext();
|
||||
if (!activeFile || !editor || !selection) {
|
||||
console.error("No active file, editor, or selection");
|
||||
console.error(t("inlineEdit.noActiveContext"));
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
@ -201,7 +203,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
||||
};
|
||||
if (!chatModel) {
|
||||
setIsSubmitting(false);
|
||||
throw new Error("Invalid chat model");
|
||||
throw new Error(t("inlineEdit.invalidChatModel"));
|
||||
}
|
||||
|
||||
const from = editor.getCursor("from");
|
||||
@ -238,7 +240,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
||||
}
|
||||
if (!response_content) {
|
||||
setIsSubmitting(false);
|
||||
throw new Error("Empty response from LLM");
|
||||
throw new Error(t("inlineEdit.emptyLLMResponse"));
|
||||
}
|
||||
|
||||
const parsedBlock = parseSmartComposeBlock(
|
||||
@ -248,14 +250,15 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
||||
|
||||
if (!activeFile || !(activeFile.path && typeof activeFile.path === 'string')) {
|
||||
setIsSubmitting(false);
|
||||
throw new Error("Invalid active file");
|
||||
throw new Error(t("inlineEdit.invalidActiveFile"));
|
||||
}
|
||||
|
||||
let fileContent: string;
|
||||
try {
|
||||
fileContent = await plugin.app.vault.cachedRead(activeFile);
|
||||
} catch (error) {
|
||||
console.error("Failed to read file:", error);
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
console.error(t("inlineEdit.readFileError"), error.message);
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
@ -268,7 +271,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
||||
);
|
||||
|
||||
if (!updatedContent) {
|
||||
console.error("Failed to apply changes");
|
||||
console.error(t("inlineEdit.applyChangesError"));
|
||||
setIsSubmitting(false);
|
||||
return;
|
||||
}
|
||||
@ -283,8 +286,9 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
||||
newContent: removeAITags(updatedContent),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error in inline edit:", error);
|
||||
} catch (err) {
|
||||
const error = err as Error;
|
||||
console.error(t("inlineEdit.inlineEditError"), error.message);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
||||
@ -1,4 +1,106 @@
|
||||
export default {
|
||||
chat: {
|
||||
stop: "Stop",
|
||||
errors: {
|
||||
failedToLoadConversation: "Failed to load conversation",
|
||||
failedToSaveHistory: "Failed to save chat history",
|
||||
failedToApplyChanges: "Failed to apply changes",
|
||||
conversationNotFound: "Conversation not found",
|
||||
fileNotFound: "File not found: {{path}}",
|
||||
failedToApplyEditChanges: "Failed to apply edit changes",
|
||||
failedToSearchAndReplace: "Failed to search and replace"
|
||||
},
|
||||
apply: {
|
||||
changesApplied: "Changes successfully applied",
|
||||
changesRejected: "User rejected changes"
|
||||
},
|
||||
search: {
|
||||
noResultsFound: "No results found for '{{query}}'"
|
||||
},
|
||||
history: {
|
||||
noConversations: "No conversations"
|
||||
},
|
||||
shortcutInfo: {
|
||||
editInline: "Edit inline",
|
||||
chatWithSelect: "Chat with selected text",
|
||||
submitWithVault: "Submit with vault"
|
||||
},
|
||||
searchResults: {
|
||||
showReferencedDocuments: "Show Referenced Documents"
|
||||
},
|
||||
LLMResponseInfoPopover: {
|
||||
header: "LLM response information",
|
||||
tokenCount: "Token count",
|
||||
promptTokens: "Prompt tokens",
|
||||
completionTokens: "Completion tokens",
|
||||
totalTokens: "Total tokens",
|
||||
model: "Model",
|
||||
estimatedPrice: "Estimated price",
|
||||
usageNotAvailable: "Usage statistics are not available for this model",
|
||||
notAvailable: "Not available"
|
||||
},
|
||||
queryProgress: {
|
||||
readingMentionableFiles: "Reading mentioned files",
|
||||
indexing: "Indexing",
|
||||
file: "file",
|
||||
chunkIndexed: "chunk indexed",
|
||||
queryingVault: "Querying the vault",
|
||||
readingRelatedFiles: "Reading related files"
|
||||
},
|
||||
reactMarkdown: {
|
||||
allow: "Allow",
|
||||
allowing: "Allowing...",
|
||||
success: "Success",
|
||||
failed: "Failed",
|
||||
switchToMode: 'Switch to "{mode}" mode',
|
||||
semanticSearchInPath: 'semantic search files "{query}" in {path}',
|
||||
webSearch: "Web search: {query}",
|
||||
searching: "Searching...",
|
||||
done: "Done",
|
||||
searchAndReplaceInPath: "Search and replace in {path}",
|
||||
applying: "Applying...",
|
||||
apply: "Apply",
|
||||
reasoning: "Reasoning",
|
||||
readFile: "Read file: {path}",
|
||||
listFiles: "List files: {path}",
|
||||
fetchUrlsContent: "Fetch URLs Content",
|
||||
fetching: "Fetching...",
|
||||
copied: "Copied",
|
||||
copy: "Copy",
|
||||
editOrApplyDiff: "{mode}: {path}",
|
||||
loading: "Loading...",
|
||||
regexSearchInPath: 'regex search files "{regex}" in {path}',
|
||||
createNewNote: "Create new note",
|
||||
copyMsg: "Copy message",
|
||||
taskCompletion: "Task Completion",
|
||||
askFollowupQuestion: "Ask Followup Question:",
|
||||
viewDetails: "View details"
|
||||
},
|
||||
input: {
|
||||
submit: "Submit",
|
||||
collectedModels: "Collected Models",
|
||||
loading: "Loading...",
|
||||
image: "Image",
|
||||
createCommand: "Create Command"
|
||||
}
|
||||
},
|
||||
inlineEdit: {
|
||||
placeholder: "Input instruction, Enter to submit, Esc to close",
|
||||
fetchModelsError: "Failed to fetch provider models:",
|
||||
submitting: "submitting...",
|
||||
submit: "submit",
|
||||
ragNotNeeded: "RAG not needed for inline edit",
|
||||
noActiveFile: "No active file",
|
||||
noActiveEditor: "No active editor",
|
||||
noTextSelected: "No text selected",
|
||||
noActiveContext: "No active file, editor, or selection",
|
||||
invalidChatModel: "Invalid chat model",
|
||||
emptyLLMResponse: "Empty response from LLM",
|
||||
invalidActiveFile: "Invalid active file",
|
||||
readFileError: "Failed to read file:",
|
||||
applyChangesError: "Failed to apply changes",
|
||||
inlineEditError: "Error in inline edit:",
|
||||
},
|
||||
prompt: {
|
||||
"title": "Prompts",
|
||||
"description": "Click + to create a new mode",
|
||||
@ -25,5 +127,16 @@ export default {
|
||||
"overrideWarning": ". This is a very advanced feature that will override all built-in prompts including tool usage, please use with caution",
|
||||
"previewSystemPrompt": "Preview System Prompt",
|
||||
"save": "Save"
|
||||
},
|
||||
command: {
|
||||
"createQuickCommand": "Create Quick Command",
|
||||
"name": "Name",
|
||||
"content": "Content",
|
||||
"createCommand": "Create Command",
|
||||
"searchPlaceholder": "Search Command...",
|
||||
"noCommandsFound": "No commands found",
|
||||
"updateCommand": "Update Command",
|
||||
"errorContentRequired": "Please enter a content for your template",
|
||||
"errorNameRequired": "Please enter a name for your template"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,108 @@
|
||||
|
||||
// 简体中文
|
||||
export default {
|
||||
chat: {
|
||||
stop: "停止",
|
||||
errors: {
|
||||
failedToLoadConversation: "加载对话失败",
|
||||
failedToSaveHistory: "保存聊天记录失败",
|
||||
failedToApplyChanges: "应用更改失败",
|
||||
conversationNotFound: "未找到对话",
|
||||
fileNotFound: "未找到文件:{{path}}",
|
||||
failedToApplyEditChanges: "应用编辑更改失败",
|
||||
failedToSearchAndReplace: "搜索和替换失败"
|
||||
},
|
||||
apply: {
|
||||
changesApplied: "更改已成功应用",
|
||||
changesRejected: "用户拒绝了更改"
|
||||
},
|
||||
search: {
|
||||
noResultsFound: "未找到 '{{query}}' 的结果"
|
||||
},
|
||||
history: {
|
||||
noConversations: "没有对话"
|
||||
},
|
||||
shortcutInfo: {
|
||||
editInline: "行内编辑",
|
||||
chatWithSelect: "与选定文本聊天",
|
||||
submitWithVault: "使用 Vault 提交"
|
||||
},
|
||||
searchResults: {
|
||||
showReferencedDocuments: "显示引用的文档"
|
||||
},
|
||||
LLMResponseInfoPopover: {
|
||||
header: "LLM 响应信息",
|
||||
tokenCount: "Token 数量",
|
||||
promptTokens: "提示 Tokens",
|
||||
completionTokens: "补全 Tokens",
|
||||
totalTokens: "总 Tokens",
|
||||
model: "模型",
|
||||
estimatedPrice: "预估价格",
|
||||
usageNotAvailable: "此模型无法获取使用统计信息",
|
||||
notAvailable: "不可用"
|
||||
},
|
||||
queryProgress: {
|
||||
readingMentionableFiles: "正在读取提及的文件",
|
||||
indexing: "正在索引",
|
||||
file: "文件",
|
||||
chunkIndexed: "块已索引",
|
||||
queryingVault: "正在查询 Vault",
|
||||
readingRelatedFiles: "正在读取相关文件"
|
||||
},
|
||||
reactMarkdown: {
|
||||
allow: "允许",
|
||||
allowing: "正在允许...",
|
||||
success: "成功",
|
||||
failed: "失败",
|
||||
switchToMode: '切换到 "{mode}" 模式',
|
||||
semanticSearchInPath: '在 {path} 中语义搜索文件 "{query}"',
|
||||
webSearch: "网页搜索:{query}",
|
||||
searching: "正在搜索...",
|
||||
done: "完成",
|
||||
searchAndReplaceInPath: "在 {path} 中搜索和替换",
|
||||
applying: "正在应用...",
|
||||
apply: "应用",
|
||||
reasoning: "推理",
|
||||
readFile: "读取文件:{path}",
|
||||
listFiles: "列出文件:{path}",
|
||||
fetchUrlsContent: "获取 URL 内容",
|
||||
fetching: "正在获取...",
|
||||
copied: "已复制",
|
||||
copy: "复制",
|
||||
editOrApplyDiff: "{mode}:{path}",
|
||||
loading: "加载中...",
|
||||
regexSearchInPath: '在 {path} 中正则搜索文件 "{regex}"',
|
||||
createNewNote: "创建新笔记",
|
||||
copyMsg: "复制消息",
|
||||
taskCompletion: "任务完成",
|
||||
askFollowupQuestion: "询问后续问题:",
|
||||
viewDetails: "查看详情"
|
||||
},
|
||||
input: {
|
||||
submit: "提交",
|
||||
collectedModels: "收集的模型",
|
||||
loading: "加载中...",
|
||||
image: "图片",
|
||||
createCommand: "创建命令"
|
||||
}
|
||||
},
|
||||
inlineEdit: {
|
||||
placeholder: "输入指令,Enter 提交,Esc 关闭",
|
||||
fetchModelsError: "获取 Provider 模型失败:",
|
||||
submitting: "提交中...",
|
||||
submit: "提交",
|
||||
ragNotNeeded: "行内编辑不需要 RAG",
|
||||
noActiveFile: "没有活动文件",
|
||||
noActiveEditor: "没有活动编辑器",
|
||||
noTextSelected: "未选择文本",
|
||||
noActiveContext: "没有活动文件、编辑器或选区",
|
||||
invalidChatModel: "无效的聊天模型",
|
||||
emptyLLMResponse: "LLM 返回空响应",
|
||||
invalidActiveFile: "无效的活动文件",
|
||||
readFileError: "读取文件失败:",
|
||||
applyChangesError: "应用更改失败",
|
||||
inlineEditError: "行内编辑出错:",
|
||||
},
|
||||
prompt: {
|
||||
"title": "模型提示词设置",
|
||||
"description": "点击 + 创建新模式",
|
||||
@ -28,5 +130,16 @@ export default {
|
||||
"overrideWarning": "。这是一个非常高级的功能,将覆盖所有内置提示,包括工具使用,请谨慎使用",
|
||||
"previewSystemPrompt": "预览系统提示",
|
||||
"save": "保存"
|
||||
},
|
||||
command: {
|
||||
"createQuickCommand": "创建快捷命令",
|
||||
"name": "名称",
|
||||
"content": "内容",
|
||||
"createCommand": "创建命令",
|
||||
"searchPlaceholder": "搜索命令...",
|
||||
"noCommandsFound": "未找到命令",
|
||||
"updateCommand": "更新命令",
|
||||
"errorContentRequired": "请输入模板内容",
|
||||
"errorNameRequired": "请输入模板名称"
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user