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 { Check, CopyIcon } from 'lucide-react'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
import { ChatAssistantMessage } from '../../types/chat'
|
import { ChatAssistantMessage } from '../../types/chat'
|
||||||
import { calculateLLMCost } from '../../utils/price-calculator'
|
import { calculateLLMCost } from '../../utils/price-calculator'
|
||||||
|
|
||||||
@ -35,7 +36,7 @@ function CopyButton({ message }: { message: ChatAssistantMessage }) {
|
|||||||
</Tooltip.Trigger>
|
</Tooltip.Trigger>
|
||||||
<Tooltip.Portal>
|
<Tooltip.Portal>
|
||||||
<Tooltip.Content className="infio-tooltip-content">
|
<Tooltip.Content className="infio-tooltip-content">
|
||||||
Copy message
|
{t('chat.reactMarkdown.copyMsg')}
|
||||||
</Tooltip.Content>
|
</Tooltip.Content>
|
||||||
</Tooltip.Portal>
|
</Tooltip.Portal>
|
||||||
</Tooltip.Root>
|
</Tooltip.Root>
|
||||||
@ -76,7 +77,7 @@ function LLMResponesInfoButton({ message }: { message: ChatAssistantMessage }) {
|
|||||||
</Tooltip.Trigger>
|
</Tooltip.Trigger>
|
||||||
<Tooltip.Portal>
|
<Tooltip.Portal>
|
||||||
<Tooltip.Content className="infio-tooltip-content">
|
<Tooltip.Content className="infio-tooltip-content">
|
||||||
View details
|
{t('chat.reactMarkdown.viewDetails')}
|
||||||
</Tooltip.Content>
|
</Tooltip.Content>
|
||||||
</Tooltip.Portal>
|
</Tooltip.Portal>
|
||||||
</Tooltip.Root>
|
</Tooltip.Root>
|
||||||
|
|||||||
@ -2,8 +2,10 @@ import * as Popover from '@radix-ui/react-popover'
|
|||||||
import { Pencil, Trash2 } from 'lucide-react'
|
import { Pencil, Trash2 } from 'lucide-react'
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
import { ChatConversationMeta } from '../../types/chat'
|
import { ChatConversationMeta } from '../../types/chat'
|
||||||
|
|
||||||
|
|
||||||
function TitleInput({
|
function TitleInput({
|
||||||
title,
|
title,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
@ -165,7 +167,7 @@ export function ChatHistory({
|
|||||||
<ul>
|
<ul>
|
||||||
{chatList.length === 0 ? (
|
{chatList.length === 0 ? (
|
||||||
<li className="infio-chat-list-dropdown-empty">
|
<li className="infio-chat-list-dropdown-empty">
|
||||||
No conversations
|
{t('chat.history.noConversations')}
|
||||||
</li>
|
</li>
|
||||||
) : (
|
) : (
|
||||||
chatList.map((chat, index) => (
|
chatList.map((chat, index) => (
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import {
|
|||||||
import { regexSearchFiles } from '../../core/ripgrep'
|
import { regexSearchFiles } from '../../core/ripgrep'
|
||||||
import { useChatHistory } from '../../hooks/use-chat-history'
|
import { useChatHistory } from '../../hooks/use-chat-history'
|
||||||
import { useCustomModes } from '../../hooks/use-custom-mode'
|
import { useCustomModes } from '../../hooks/use-custom-mode'
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
import { ApplyStatus, ToolArgs } from '../../types/apply'
|
import { ApplyStatus, ToolArgs } from '../../types/apply'
|
||||||
import { ChatMessage, ChatUserMessage } from '../../types/chat'
|
import { ChatMessage, ChatUserMessage } from '../../types/chat'
|
||||||
import {
|
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[]>([])
|
const [selectedSerializedNodes, setSelectedSerializedNodes] = useState<BaseSerializedNode[]>([])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -216,7 +217,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
abortActiveStreams()
|
abortActiveStreams()
|
||||||
const conversation = await getChatMessagesById(conversationId)
|
const conversation = await getChatMessagesById(conversationId)
|
||||||
if (!conversation) {
|
if (!conversation) {
|
||||||
throw new Error('Conversation not found')
|
throw new Error(t('chat.errors.conversationNotFound'))
|
||||||
}
|
}
|
||||||
setCurrentConversationId(conversationId)
|
setCurrentConversationId(conversationId)
|
||||||
setChatMessages(conversation)
|
setChatMessages(conversation)
|
||||||
@ -227,8 +228,8 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
type: 'idle',
|
type: 'idle',
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
new Notice('Failed to load conversation')
|
new Notice(t('chat.errors.failedToLoadConversation'))
|
||||||
console.error('Failed to load conversation', error)
|
console.error(t('chat.errors.failedToLoadConversation'), error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1031,7 +1032,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
{submitMutation.isPending && (
|
{submitMutation.isPending && (
|
||||||
<button onClick={abortActiveStreams} className="infio-stop-gen-btn">
|
<button onClick={abortActiveStreams} className="infio-stop-gen-btn">
|
||||||
<CircleStop size={16} />
|
<CircleStop size={16} />
|
||||||
<div>Stop generation</div>
|
<div>{t('chat.stop')}</div>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|||||||
|
|
||||||
import { TemplateContent } from '../../database/schema'
|
import { TemplateContent } from '../../database/schema'
|
||||||
import { useCommands } from '../../hooks/use-commands'
|
import { useCommands } from '../../hooks/use-commands'
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
|
|
||||||
import LexicalContentEditable from './chat-input/LexicalContentEditable'
|
import LexicalContentEditable from './chat-input/LexicalContentEditable'
|
||||||
|
|
||||||
@ -107,11 +108,11 @@ const CommandsView = (
|
|||||||
const serializedEditorState = editorRef.current.toJSON()
|
const serializedEditorState = editorRef.current.toJSON()
|
||||||
const nodes = serializedEditorState.editorState.root.children
|
const nodes = serializedEditorState.editorState.root.children
|
||||||
if (nodes.length === 0) {
|
if (nodes.length === 0) {
|
||||||
new Notice('Please enter a content for your template')
|
new Notice(String(t('command.errorContentRequired')))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (newCommandName.trim().length === 0) {
|
if (newCommandName.trim().length === 0) {
|
||||||
new Notice('Please enter a name for your template')
|
new Notice(String(t('command.errorNameRequired')))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,13 +141,13 @@ const CommandsView = (
|
|||||||
const nameInput = nameInputRefs.current.get(id)
|
const nameInput = nameInputRefs.current.get(id)
|
||||||
const currContentEditorRef = contentEditorRefs.current.get(id)
|
const currContentEditorRef = contentEditorRefs.current.get(id)
|
||||||
if (!currContentEditorRef) {
|
if (!currContentEditorRef) {
|
||||||
new Notice('Please enter a content for your template')
|
new Notice(String(t('command.errorContentRequired')))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const serializedEditorState = currContentEditorRef.toJSON()
|
const serializedEditorState = currContentEditorRef.toJSON()
|
||||||
const nodes = serializedEditorState.editorState.root.children
|
const nodes = serializedEditorState.editorState.root.children
|
||||||
if (nodes.length === 0) {
|
if (nodes.length === 0) {
|
||||||
new Notice('Please enter a content for your template')
|
new Notice(String(t('command.errorContentRequired')))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await updateCommand(
|
await updateCommand(
|
||||||
@ -190,15 +191,15 @@ const CommandsView = (
|
|||||||
{/* header */}
|
{/* header */}
|
||||||
<div className="infio-commands-header">
|
<div className="infio-commands-header">
|
||||||
<div className="infio-commands-new">
|
<div className="infio-commands-new">
|
||||||
<h2 className="infio-commands-header-title">Create Quick Command</h2>
|
<h2 className="infio-commands-header-title">{t('command.createQuickCommand')}</h2>
|
||||||
<div className="infio-commands-label">Name</div>
|
<div className="infio-commands-label">{t('command.name')}</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={newCommandName}
|
value={newCommandName}
|
||||||
onChange={(e) => setNewCommandName(e.target.value)}
|
onChange={(e) => setNewCommandName(e.target.value)}
|
||||||
className="infio-commands-input"
|
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">
|
<div className="infio-commands-textarea">
|
||||||
<LexicalContentEditable
|
<LexicalContentEditable
|
||||||
initialEditorState={initialEditorState}
|
initialEditorState={initialEditorState}
|
||||||
@ -211,7 +212,7 @@ const CommandsView = (
|
|||||||
className="infio-commands-add-btn"
|
className="infio-commands-add-btn"
|
||||||
disabled={!newCommandName.trim()}
|
disabled={!newCommandName.trim()}
|
||||||
>
|
>
|
||||||
<span>Create Command</span>
|
<span>{t('command.createCommand')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -221,7 +222,7 @@ const CommandsView = (
|
|||||||
<Search size={18} className="infio-commands-search-icon" />
|
<Search size={18} className="infio-commands-search-icon" />
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search Command..."
|
placeholder={t('command.searchPlaceholder')}
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
className="infio-commands-search-input"
|
className="infio-commands-search-input"
|
||||||
@ -232,7 +233,7 @@ const CommandsView = (
|
|||||||
<div className="infio-commands-list">
|
<div className="infio-commands-list">
|
||||||
{filteredCommands.length === 0 ? (
|
{filteredCommands.length === 0 ? (
|
||||||
<div className="infio-commands-empty">
|
<div className="infio-commands-empty">
|
||||||
<p>No commands found</p>
|
<p>{t('command.noCommandsFound')}</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
filteredCommands.map(command => (
|
filteredCommands.map(command => (
|
||||||
@ -260,7 +261,7 @@ const CommandsView = (
|
|||||||
onClick={() => handleSaveEdit(command.id)}
|
onClick={() => handleSaveEdit(command.id)}
|
||||||
className="infio-commands-add-btn"
|
className="infio-commands-add-btn"
|
||||||
>
|
>
|
||||||
<span>Update Command</span>
|
<span>{t('command.updateCommand')}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -9,13 +9,12 @@ import { useRAG } from '../../contexts/RAGContext';
|
|||||||
import { useSettings } from '../../contexts/SettingsContext';
|
import { useSettings } from '../../contexts/SettingsContext';
|
||||||
import { CustomMode, GroupEntry, ToolGroup } from '../../database/json/custom-mode/types';
|
import { CustomMode, GroupEntry, ToolGroup } from '../../database/json/custom-mode/types';
|
||||||
import { useCustomModes } from '../../hooks/use-custom-mode';
|
import { useCustomModes } from '../../hooks/use-custom-mode';
|
||||||
|
import { t } from '../../lang/helpers';
|
||||||
import { PreviewView, PreviewViewState } from '../../PreviewView';
|
import { PreviewView, PreviewViewState } from '../../PreviewView';
|
||||||
import { modes as buildinModes } from '../../utils/modes';
|
import { modes as buildinModes } from '../../utils/modes';
|
||||||
import { openOrCreateMarkdownFile } from '../../utils/obsidian';
|
import { openOrCreateMarkdownFile } from '../../utils/obsidian';
|
||||||
import { PromptGenerator, getFullLanguageName } from '../../utils/prompt-generator';
|
import { PromptGenerator, getFullLanguageName } from '../../utils/prompt-generator';
|
||||||
|
|
||||||
import { t } from '../../lang/helpers';
|
|
||||||
|
|
||||||
const CustomModeView = () => {
|
const CustomModeView = () => {
|
||||||
const app = useApp()
|
const app = useApp()
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
Info,
|
Info,
|
||||||
} from 'lucide-react'
|
} from 'lucide-react'
|
||||||
|
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
import { ResponseUsage } from '../../types/llm/response'
|
import { ResponseUsage } from '../../types/llm/response'
|
||||||
|
|
||||||
type LLMResponseInfoProps = {
|
type LLMResponseInfoProps = {
|
||||||
@ -30,27 +31,27 @@ export default function LLMResponseInfoPopover({
|
|||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
{usage ? (
|
{usage ? (
|
||||||
<Popover.Content className="infio-chat-popover-content infio-llm-info-content">
|
<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">
|
||||||
<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-tokens-grid">
|
||||||
<div className="infio-llm-info-token-row">
|
<div className="infio-llm-info-token-row">
|
||||||
<ArrowUp className="infio-llm-info-icon--input" />
|
<ArrowUp className="infio-llm-info-icon--input" />
|
||||||
<span>Input:</span>
|
<span>{t('chat.LLMResponseInfoPopover.promptTokens')}</span>
|
||||||
<span className="infio-llm-info-token-value">
|
<span className="infio-llm-info-token-value">
|
||||||
{usage.prompt_tokens}
|
{usage.prompt_tokens}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-llm-info-token-row">
|
<div className="infio-llm-info-token-row">
|
||||||
<ArrowDown className="infio-llm-info-icon--output" />
|
<ArrowDown className="infio-llm-info-icon--output" />
|
||||||
<span>Output:</span>
|
<span>{t('chat.LLMResponseInfoPopover.completionTokens')}</span>
|
||||||
<span className="infio-llm-info-token-value">
|
<span className="infio-llm-info-token-value">
|
||||||
{usage.completion_tokens}
|
{usage.completion_tokens}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-llm-info-token-row infio-llm-info-token-total">
|
<div className="infio-llm-info-token-row infio-llm-info-token-total">
|
||||||
<ArrowRightLeft className="infio-llm-info-icon--total" />
|
<ArrowRightLeft className="infio-llm-info-icon--total" />
|
||||||
<span>Total:</span>
|
<span>{t('chat.LLMResponseInfoPopover.totalTokens')}</span>
|
||||||
<span className="infio-llm-info-token-value">
|
<span className="infio-llm-info-token-value">
|
||||||
{usage.total_tokens}
|
{usage.total_tokens}
|
||||||
</span>
|
</span>
|
||||||
@ -59,24 +60,24 @@ export default function LLMResponseInfoPopover({
|
|||||||
</div>
|
</div>
|
||||||
<div className="infio-llm-info-footer-row">
|
<div className="infio-llm-info-footer-row">
|
||||||
<Coins className="infio-llm-info-icon--footer" />
|
<Coins className="infio-llm-info-icon--footer" />
|
||||||
<span>Estimated price:</span>
|
<span>{t('chat.LLMResponseInfoPopover.estimatedPrice')}</span>
|
||||||
<span className="infio-llm-info-footer-value">
|
<span className="infio-llm-info-footer-value">
|
||||||
{estimatedPrice === null
|
{estimatedPrice === null
|
||||||
? 'Not available'
|
? t('chat.LLMResponseInfoPopover.notAvailable')
|
||||||
: `$${estimatedPrice.toFixed(4)}`}
|
: `$${estimatedPrice.toFixed(4)}`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-llm-info-footer-row">
|
<div className="infio-llm-info-footer-row">
|
||||||
<Cpu className="infio-llm-info-icon--footer" />
|
<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">
|
<span className="infio-llm-info-footer-value infio-llm-info-model">
|
||||||
{model ?? 'Not available'}
|
{model ?? t('chat.LLMResponseInfoPopover.notAvailable')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Popover.Content>
|
</Popover.Content>
|
||||||
) : (
|
) : (
|
||||||
<Popover.Content className="infio-chat-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.Content>
|
||||||
)}
|
)}
|
||||||
</Popover.Root>
|
</Popover.Root>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Check, Diff, Loader2, X } from 'lucide-react'
|
|||||||
import { PropsWithChildren, useState } from 'react'
|
import { PropsWithChildren, useState } from 'react'
|
||||||
|
|
||||||
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
||||||
|
|
||||||
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
||||||
@ -55,23 +56,23 @@ export default function MarkdownApplyDiffBlock({
|
|||||||
{
|
{
|
||||||
!finish ? (
|
!finish ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="spinner" size={14} /> Loading...
|
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.loading')}
|
||||||
</>
|
</>
|
||||||
) : applyStatus === ApplyStatus.Idle ? (
|
) : applyStatus === ApplyStatus.Idle ? (
|
||||||
applying ? (
|
applying ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="spinner" size={14} /> Applying...
|
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.applying')}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
'Apply'
|
t('chat.reactMarkdown.apply')
|
||||||
)
|
)
|
||||||
) : applyStatus === ApplyStatus.Applied ? (
|
) : 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>
|
</button>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Check, CopyIcon, Edit, Loader2, X } from 'lucide-react'
|
|||||||
import { PropsWithChildren, useMemo, useState } from 'react'
|
import { PropsWithChildren, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
||||||
|
|
||||||
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
||||||
@ -63,7 +64,7 @@ export default function MarkdownEditFileBlock({
|
|||||||
{path && (
|
{path && (
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<Edit size={10} className="infio-chat-code-block-header-icon" />
|
<Edit size={10} className="infio-chat-code-block-header-icon" />
|
||||||
{mode}: {path}
|
{t('chat.reactMarkdown.editOrApplyDiff').replace('{mode}', mode).replace('{path}', path)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={'infio-chat-code-block-header-button'}>
|
<div className={'infio-chat-code-block-header-button'}>
|
||||||
@ -74,34 +75,34 @@ export default function MarkdownEditFileBlock({
|
|||||||
>
|
>
|
||||||
{copied ? (
|
{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>
|
||||||
<button
|
<button
|
||||||
onClick={handleApply}
|
onClick={handleApply}
|
||||||
style={{ color: '#008000' }}
|
className="infio-apply-button"
|
||||||
disabled={applyStatus !== ApplyStatus.Idle || applying}
|
disabled={applyStatus !== ApplyStatus.Idle || applying}
|
||||||
>
|
>
|
||||||
{applyStatus === ApplyStatus.Idle ? (
|
{applyStatus === ApplyStatus.Idle ? (
|
||||||
applying ? (
|
applying ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="spinner" size={14} /> Applying...
|
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.applying')}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
'Apply'
|
t('chat.reactMarkdown.apply')
|
||||||
)
|
)
|
||||||
) : applyStatus === ApplyStatus.Applied ? (
|
) : 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>
|
</button>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Check, ChevronDown, ChevronRight, Globe, Loader2, X } from 'lucide-react'
|
import { Check, ChevronDown, ChevronRight, Globe, Loader2, X } from 'lucide-react'
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
import React, { useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, FetchUrlsContentToolArgs } from "../../../types/apply"
|
import { ApplyStatus, FetchUrlsContentToolArgs } from "../../../types/apply"
|
||||||
|
|
||||||
export default function MarkdownFetchUrlsContentBlock({
|
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">
|
||||||
<div className="infio-chat-code-block-header-filename">
|
<div className="infio-chat-code-block-header-filename">
|
||||||
<Globe size={10} className="infio-chat-code-block-header-icon" />
|
<Globe size={10} className="infio-chat-code-block-header-icon" />
|
||||||
Fetch URLs Content
|
{t('chat.reactMarkdown.fetchUrlsContent')}
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-chat-code-block-header-button">
|
<div className="infio-chat-code-block-header-button">
|
||||||
<button
|
<button
|
||||||
@ -48,15 +49,15 @@ export default function MarkdownFetchUrlsContentBlock({
|
|||||||
{
|
{
|
||||||
!finish || applyStatus === ApplyStatus.Idle ? (
|
!finish || applyStatus === ApplyStatus.Idle ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="spinner" size={14} /> Fetching...
|
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.fetching')}
|
||||||
</>
|
</>
|
||||||
) : applyStatus === ApplyStatus.Applied ? (
|
) : 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>
|
</button>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { FolderOpen } from 'lucide-react'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useApp } from "../../../contexts/AppContext"
|
import { useApp } from "../../../contexts/AppContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, ListFilesToolArgs } from "../../../types/apply"
|
import { ApplyStatus, ListFilesToolArgs } from "../../../types/apply"
|
||||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
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'}>
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<FolderOpen size={14} className="infio-chat-code-block-header-icon" />
|
<FolderOpen size={14} className="infio-chat-code-block-header-icon" />
|
||||||
List files: {path}
|
{t('chat.reactMarkdown.listFiles').replace('{path}', path)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { ExternalLink } from 'lucide-react'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useApp } from "../../../contexts/AppContext"
|
import { useApp } from "../../../contexts/AppContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, ReadFileToolArgs } from "../../../types/apply"
|
import { ApplyStatus, ReadFileToolArgs } from "../../../types/apply"
|
||||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
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'}>
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<ExternalLink size={10} className="infio-chat-code-block-header-icon" />
|
<ExternalLink size={10} className="infio-chat-code-block-header-icon" />
|
||||||
Read file: {path}
|
{t('chat.reactMarkdown.readFile').replace('{path}', path)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { ChevronDown, ChevronRight, Brain } from 'lucide-react'
|
|||||||
import { PropsWithChildren, useEffect, useRef, useState } from 'react'
|
import { PropsWithChildren, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
|
|
||||||
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
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'}>
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<Brain size={10} className="infio-chat-code-block-header-icon" />
|
<Brain size={10} className="infio-chat-code-block-header-icon" />
|
||||||
Reasoning
|
{t('chat.reactMarkdown.reasoning')}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
className="clickable-icon infio-chat-list-dropdown"
|
className="clickable-icon infio-chat-list-dropdown"
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { FileSearch } from 'lucide-react'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useApp } from "../../../contexts/AppContext"
|
import { useApp } from "../../../contexts/AppContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, RegexSearchFilesToolArgs } from "../../../types/apply"
|
import { ApplyStatus, RegexSearchFilesToolArgs } from "../../../types/apply"
|
||||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
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'}>
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<FileSearch size={14} className="infio-chat-code-block-header-icon" />
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import React from 'react'
|
|||||||
|
|
||||||
import { useApp } from '../../../contexts/AppContext'
|
import { useApp } from '../../../contexts/AppContext'
|
||||||
import { useDarkModeContext } from '../../../contexts/DarkModeContext'
|
import { useDarkModeContext } from '../../../contexts/DarkModeContext'
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, SearchAndReplaceToolArgs } from '../../../types/apply'
|
import { ApplyStatus, SearchAndReplaceToolArgs } from '../../../types/apply'
|
||||||
import { openMarkdownFile } from '../../../utils/obsidian'
|
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'}>
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<Replace size={10} className="infio-chat-code-block-header-icon" />
|
<Replace size={10} className="infio-chat-code-block-header-icon" />
|
||||||
Search and replace in {path}
|
{t('chat.reactMarkdown.searchAndReplaceInPath').replace('{path}', path)}
|
||||||
</div>
|
</div>
|
||||||
<div className={'infio-chat-code-block-header-button'}>
|
<div className={'infio-chat-code-block-header-button'}>
|
||||||
<button
|
<button
|
||||||
@ -66,18 +67,18 @@ export default function MarkdownSearchAndReplace({
|
|||||||
) : applyStatus === ApplyStatus.Idle ? (
|
) : applyStatus === ApplyStatus.Idle ? (
|
||||||
applying ? (
|
applying ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="spinner" size={14} /> Applying...
|
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.applying')}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
'Apply'
|
t('chat.reactMarkdown.apply')
|
||||||
)
|
)
|
||||||
) : applyStatus === ApplyStatus.Applied ? (
|
) : 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>
|
</button>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Check, Loader2, Search, X } from 'lucide-react'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useSettings } from "../../../contexts/SettingsContext"
|
import { useSettings } from "../../../contexts/SettingsContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, SearchWebToolArgs } from "../../../types/apply"
|
import { ApplyStatus, SearchWebToolArgs } from "../../../types/apply"
|
||||||
|
|
||||||
export default function MarkdownWebSearchBlock({
|
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'}>
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<Search size={14} className="infio-chat-code-block-header-icon" />
|
<Search size={14} className="infio-chat-code-block-header-icon" />
|
||||||
Web search: {query}
|
{t('chat.reactMarkdown.webSearch').replace('{query}', query)}
|
||||||
</div>
|
</div>
|
||||||
<div className={'infio-chat-code-block-header-button'}>
|
<div className={'infio-chat-code-block-header-button'}>
|
||||||
<button
|
<button
|
||||||
@ -56,15 +57,15 @@ export default function MarkdownWebSearchBlock({
|
|||||||
{
|
{
|
||||||
!finish || applyStatus === ApplyStatus.Idle ? (
|
!finish || applyStatus === ApplyStatus.Idle ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="spinner" size={14} /> Searching...
|
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.searching')}
|
||||||
</>
|
</>
|
||||||
) : applyStatus === ApplyStatus.Applied ? (
|
) : 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>
|
</button>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { FileSearch } from 'lucide-react'
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useApp } from "../../../contexts/AppContext"
|
import { useApp } from "../../../contexts/AppContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, SemanticSearchFilesToolArgs } from "../../../types/apply"
|
import { ApplyStatus, SemanticSearchFilesToolArgs } from "../../../types/apply"
|
||||||
import { openMarkdownFile } from "../../../utils/obsidian"
|
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'}>
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<FileSearch size={14} className="infio-chat-code-block-header-icon" />
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { Check, Loader2, Settings2, X } from 'lucide-react'
|
|||||||
import { PropsWithChildren, useState } from 'react'
|
import { PropsWithChildren, useState } from 'react'
|
||||||
|
|
||||||
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
import { useDarkModeContext } from "../../../contexts/DarkModeContext"
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
import { ApplyStatus, ToolArgs } from "../../../types/apply"
|
||||||
|
|
||||||
import { MemoizedSyntaxHighlighterWrapper } from "./SyntaxHighlighterWrapper"
|
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'}>
|
||||||
<div className={'infio-chat-code-block-header-filename'}>
|
<div className={'infio-chat-code-block-header-filename'}>
|
||||||
<Settings2 size={10} className="infio-chat-code-block-header-icon" />
|
<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>
|
||||||
<div className={'infio-chat-code-block-header-button'}>
|
<div className={'infio-chat-code-block-header-button'}>
|
||||||
<button
|
<button
|
||||||
@ -51,18 +52,18 @@ export default function MarkdownSwitchModeBlock({
|
|||||||
{applyStatus === ApplyStatus.Idle ? (
|
{applyStatus === ApplyStatus.Idle ? (
|
||||||
applying ? (
|
applying ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="spinner" size={14} /> Allowing...
|
<Loader2 className="spinner" size={14} /> {t('chat.reactMarkdown.allowing')}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
'Allow'
|
t('chat.reactMarkdown.allow')
|
||||||
)
|
)
|
||||||
) : applyStatus === ApplyStatus.Applied ? (
|
) : 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>
|
</button>
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import ReactMarkdown from 'react-markdown';
|
|||||||
import rehypeRaw from 'rehype-raw';
|
import rehypeRaw from 'rehype-raw';
|
||||||
import { useApp } from 'src/contexts/AppContext';
|
import { useApp } from 'src/contexts/AppContext';
|
||||||
|
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
|
|
||||||
function CopyButton({ message }: { message: string }) {
|
function CopyButton({ message }: { message: string }) {
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ function CopyButton({ message }: { message: string }) {
|
|||||||
</Tooltip.Trigger>
|
</Tooltip.Trigger>
|
||||||
<Tooltip.Portal>
|
<Tooltip.Portal>
|
||||||
<Tooltip.Content className="infio-tooltip-content">
|
<Tooltip.Content className="infio-tooltip-content">
|
||||||
Copy message
|
{t('chat.reactMarkdown.copyMsg')}
|
||||||
</Tooltip.Content>
|
</Tooltip.Content>
|
||||||
</Tooltip.Portal>
|
</Tooltip.Portal>
|
||||||
</Tooltip.Root>
|
</Tooltip.Root>
|
||||||
@ -77,7 +79,7 @@ function CreateNewFileButton({ message }: { message: string }) {
|
|||||||
</Tooltip.Trigger>
|
</Tooltip.Trigger>
|
||||||
<Tooltip.Portal>
|
<Tooltip.Portal>
|
||||||
<Tooltip.Content className="infio-tooltip-content">
|
<Tooltip.Content className="infio-tooltip-content">
|
||||||
Create new note
|
{t('chat.reactMarkdown.createNewNote')}
|
||||||
</Tooltip.Content>
|
</Tooltip.Content>
|
||||||
</Tooltip.Portal>
|
</Tooltip.Portal>
|
||||||
</Tooltip.Root>
|
</Tooltip.Root>
|
||||||
@ -123,9 +125,9 @@ const MarkdownWithIcons = ({
|
|||||||
|
|
||||||
switch (iconName) {
|
switch (iconName) {
|
||||||
case 'ask_followup_question':
|
case 'ask_followup_question':
|
||||||
return 'Ask Followup Question:';
|
return t('chat.reactMarkdown.askFollowupQuestion');
|
||||||
case 'attempt_completion':
|
case 'attempt_completion':
|
||||||
return 'Task Completion';
|
return t('chat.reactMarkdown.taskCompletion');
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { SelectVector } from '../../database/schema'
|
import { SelectVector } from '../../database/schema'
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
export type QueryProgressState =
|
export type QueryProgressState =
|
||||||
| {
|
| {
|
||||||
type: 'reading-mentionables'
|
type: 'reading-mentionables'
|
||||||
@ -38,7 +38,7 @@ export default function QueryProgress({
|
|||||||
return (
|
return (
|
||||||
<div className="infio-query-progress">
|
<div className="infio-query-progress">
|
||||||
<p>
|
<p>
|
||||||
Reading mentioned files
|
{t('chat.queryProgress.readingMentionableFiles')}
|
||||||
<DotLoader />
|
<DotLoader />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -47,17 +47,17 @@ export default function QueryProgress({
|
|||||||
return (
|
return (
|
||||||
<div className="infio-query-progress">
|
<div className="infio-query-progress">
|
||||||
<p>
|
<p>
|
||||||
{`Indexing ${state.indexProgress.totalFiles} file`}
|
{`${t('chat.queryProgress.indexing')} ${state.indexProgress.totalFiles} ${t('chat.queryProgress.file')}`}
|
||||||
<DotLoader />
|
<DotLoader />
|
||||||
</p>
|
</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>
|
</div>
|
||||||
)
|
)
|
||||||
case 'querying':
|
case 'querying':
|
||||||
return (
|
return (
|
||||||
<div className="infio-query-progress">
|
<div className="infio-query-progress">
|
||||||
<p>
|
<p>
|
||||||
Querying the vault
|
{t('chat.queryProgress.queryingVault')}
|
||||||
<DotLoader />
|
<DotLoader />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -66,7 +66,7 @@ export default function QueryProgress({
|
|||||||
return (
|
return (
|
||||||
<div className="infio-query-progress">
|
<div className="infio-query-progress">
|
||||||
<p>
|
<p>
|
||||||
Reading related files
|
{t('chat.queryProgress.readingRelatedFiles')}
|
||||||
<DotLoader />
|
<DotLoader />
|
||||||
</p>
|
</p>
|
||||||
{state.queryResult.map((result) => (
|
{state.queryResult.map((result) => (
|
||||||
|
|||||||
@ -1,20 +1,22 @@
|
|||||||
import { Platform } from 'obsidian';
|
import { Platform } from 'obsidian';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
|
|
||||||
const ShortcutInfo: React.FC = () => {
|
const ShortcutInfo: React.FC = () => {
|
||||||
const modKey = Platform.isMacOS ? 'Cmd' : 'Ctrl';
|
const modKey = Platform.isMacOS ? 'Cmd' : 'Ctrl';
|
||||||
|
|
||||||
const shortcuts = [
|
const shortcuts = [
|
||||||
{
|
{
|
||||||
label: 'Edit inline',
|
label: t('chat.shortcutInfo.editInline'),
|
||||||
shortcut: `${modKey}+Shift+K`,
|
shortcut: `${modKey}+Shift+K`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Chat with select',
|
label: t('chat.shortcutInfo.chatWithSelect'),
|
||||||
shortcut: `${modKey}+Shift+L`,
|
shortcut: `${modKey}+Shift+L`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Submit with vault',
|
label: t('chat.shortcutInfo.submitWithVault'),
|
||||||
shortcut: `${modKey}+Shift+Enter`,
|
shortcut: `${modKey}+Shift+Enter`,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@ -5,67 +5,69 @@ import { useState } from 'react'
|
|||||||
|
|
||||||
import { useApp } from '../../contexts/AppContext'
|
import { useApp } from '../../contexts/AppContext'
|
||||||
import { SelectVector } from '../../database/schema'
|
import { SelectVector } from '../../database/schema'
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
import { openMarkdownFile } from '../../utils/obsidian'
|
import { openMarkdownFile } from '../../utils/obsidian'
|
||||||
|
|
||||||
function SimiliartySearchItem({
|
function SimiliartySearchItem({
|
||||||
chunk,
|
chunk,
|
||||||
}: {
|
}: {
|
||||||
chunk: Omit<SelectVector, 'embedding'> & {
|
chunk: Omit<SelectVector, 'embedding'> & {
|
||||||
similarity: number
|
similarity: number
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
const app = useApp()
|
const app = useApp()
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
openMarkdownFile(app, chunk.path, chunk.metadata.startLine)
|
openMarkdownFile(app, chunk.path, chunk.metadata.startLine)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div onClick={handleClick} className="infio-similarity-search-item">
|
<div onClick={handleClick} className="infio-similarity-search-item">
|
||||||
<div className="infio-similarity-search-item__similarity">
|
<div className="infio-similarity-search-item__similarity">
|
||||||
{chunk.similarity.toFixed(3)}
|
{chunk.similarity.toFixed(3)}
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-similarity-search-item__path">
|
<div className="infio-similarity-search-item__path">
|
||||||
{path.basename(chunk.path)}
|
{path.basename(chunk.path)}
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-similarity-search-item__line-numbers">
|
<div className="infio-similarity-search-item__line-numbers">
|
||||||
{`${chunk.metadata.startLine} - ${chunk.metadata.endLine}`}
|
{`${chunk.metadata.startLine} - ${chunk.metadata.endLine}`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SimilaritySearchResults({
|
export default function SimilaritySearchResults({
|
||||||
similaritySearchResults,
|
similaritySearchResults,
|
||||||
}: {
|
}: {
|
||||||
similaritySearchResults: (Omit<SelectVector, 'embedding'> & {
|
similaritySearchResults: (Omit<SelectVector, 'embedding'> & {
|
||||||
similarity: number
|
similarity: number
|
||||||
})[]
|
})[]
|
||||||
}) {
|
}) {
|
||||||
const [isOpen, setIsOpen] = useState(false)
|
const [isOpen, setIsOpen] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="infio-similarity-search-results">
|
<div className="infio-similarity-search-results">
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpen(!isOpen)
|
setIsOpen(!isOpen)
|
||||||
}}
|
}}
|
||||||
className="infio-similarity-search-results__trigger"
|
className="infio-similarity-search-results__trigger"
|
||||||
>
|
>
|
||||||
{isOpen ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
|
{isOpen ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
|
||||||
<div>Show Referenced Documents ({similaritySearchResults.length})</div>
|
<div>
|
||||||
</div>
|
{t('chat.searchResults.showReferencedDocuments')} ({similaritySearchResults.length})</div>
|
||||||
{isOpen && (
|
</div>
|
||||||
<div
|
{isOpen && (
|
||||||
style={{
|
<div
|
||||||
display: 'flex',
|
style={{
|
||||||
flexDirection: 'column',
|
display: 'flex',
|
||||||
}}
|
flexDirection: 'column',
|
||||||
>
|
}}
|
||||||
{similaritySearchResults.map((chunk) => (
|
>
|
||||||
<SimiliartySearchItem key={chunk.id} chunk={chunk} />
|
{similaritySearchResults.map((chunk) => (
|
||||||
))}
|
<SimiliartySearchItem key={chunk.id} chunk={chunk} />
|
||||||
</div>
|
))}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
)
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { ImageIcon } from 'lucide-react'
|
|||||||
import { TFile } from 'obsidian'
|
import { TFile } from 'obsidian'
|
||||||
|
|
||||||
import { useApp } from '../../../contexts/AppContext'
|
import { useApp } from '../../../contexts/AppContext'
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ImageSelectorModal } from '../../modals/ImageSelectorModal'
|
import { ImageSelectorModal } from '../../modals/ImageSelectorModal'
|
||||||
|
|
||||||
export function ImageUploadButton({
|
export function ImageUploadButton({
|
||||||
@ -34,7 +35,7 @@ export function ImageUploadButton({
|
|||||||
<div className="infio-chat-user-input-submit-button-icons">
|
<div className="infio-chat-user-input-submit-button-icons">
|
||||||
<ImageIcon size={12} />
|
<ImageIcon size={12} />
|
||||||
</div>
|
</div>
|
||||||
<div>Image</div>
|
<div>{t('chat.input.image')}</div>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { ChevronDown, ChevronUp, Star, StarOff } from 'lucide-react'
|
|||||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
|
||||||
import { useSettings } from '../../../contexts/SettingsContext'
|
import { useSettings } from '../../../contexts/SettingsContext'
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
import { ApiProvider } from '../../../types/llm/model'
|
import { ApiProvider } from '../../../types/llm/model'
|
||||||
import { GetAllProviders, GetProviderModelIds } from "../../../utils/api"
|
import { GetAllProviders, GetProviderModelIds } from "../../../utils/api"
|
||||||
|
|
||||||
@ -256,7 +257,7 @@ export function ModelSelect() {
|
|||||||
{settings.collectedChatModels?.length > 0 && (
|
{settings.collectedChatModels?.length > 0 && (
|
||||||
<div className="infio-model-section">
|
<div className="infio-model-section">
|
||||||
<div className="infio-model-section-title">
|
<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>
|
</div>
|
||||||
<ul className="infio-collected-models-list">
|
<ul className="infio-collected-models-list">
|
||||||
{settings.collectedChatModels.map((collectedModel, index) => (
|
{settings.collectedChatModels.map((collectedModel, index) => (
|
||||||
@ -415,7 +416,7 @@ export function ModelSelect() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="infio-loading">loading...</div>
|
<div className="infio-loading">{t('chat.input.loading')}</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="infio-model-section">
|
<div className="infio-model-section">
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import { CornerDownLeftIcon } from 'lucide-react'
|
import { CornerDownLeftIcon } from 'lucide-react'
|
||||||
|
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
|
|
||||||
export function SubmitButton({ onClick }: { onClick: () => void }) {
|
export function SubmitButton({ onClick }: { onClick: () => void }) {
|
||||||
return (
|
return (
|
||||||
<button className="infio-chat-user-input-submit-button" onClick={onClick}>
|
<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">
|
<div className="infio-chat-user-input-submit-button-icons">
|
||||||
<CornerDownLeftIcon size={12} />
|
<CornerDownLeftIcon size={12} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import {
|
|||||||
} from 'lexical'
|
} from 'lexical'
|
||||||
import { CSSProperties, useCallback, useEffect, useRef, useState } from 'react'
|
import { CSSProperties, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
|
|
||||||
|
import { t } from '../../../../../lang/helpers'
|
||||||
|
|
||||||
export default function CreateCommandPopoverPlugin({
|
export default function CreateCommandPopoverPlugin({
|
||||||
anchorElement,
|
anchorElement,
|
||||||
contentEditableElement,
|
contentEditableElement,
|
||||||
@ -121,7 +123,7 @@ export default function CreateCommandPopoverPlugin({
|
|||||||
setIsPopoverOpen(false)
|
setIsPopoverOpen(false)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
create command
|
{t('chat.input.createCommand')}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
|||||||
|
|
||||||
import { APPLY_VIEW_TYPE } from '../../constants';
|
import { APPLY_VIEW_TYPE } from '../../constants';
|
||||||
import LLMManager from '../../core/llm/manager';
|
import LLMManager from '../../core/llm/manager';
|
||||||
|
import { t } from '../../lang/helpers';
|
||||||
import { InfioSettings } from '../../types/settings';
|
import { InfioSettings } from '../../types/settings';
|
||||||
import { GetProviderModelIds } from '../../utils/api';
|
import { GetProviderModelIds } from '../../utils/api';
|
||||||
import { ApplyEditToFile } from '../../utils/apply';
|
import { ApplyEditToFile } from '../../utils/apply';
|
||||||
@ -52,7 +53,7 @@ const InputArea: React.FC<InputAreaProps> = ({ value, onChange, handleSubmit, ha
|
|||||||
<textarea
|
<textarea
|
||||||
ref={textareaRef}
|
ref={textareaRef}
|
||||||
className="infio-ai-block-content"
|
className="infio-ai-block-content"
|
||||||
placeholder="Input instruction, Enter to submit, Esc to close"
|
placeholder={t('inlineEdit.placeholder')}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={(e) => onChange(e.target.value)}
|
onChange={(e) => onChange(e.target.value)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
@ -83,8 +84,9 @@ const ControlArea: React.FC<ControlAreaProps> = ({
|
|||||||
try {
|
try {
|
||||||
const models = await GetProviderModelIds(settings.chatModelProvider);
|
const models = await GetProviderModelIds(settings.chatModelProvider);
|
||||||
setProviderModels(models);
|
setProviderModels(models);
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Failed to fetch provider models:", error);
|
const error = err as Error;
|
||||||
|
console.error(t("inlineEdit.fetchModelsError"), error.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchModels();
|
fetchModels();
|
||||||
@ -109,9 +111,9 @@ const ControlArea: React.FC<ControlAreaProps> = ({
|
|||||||
onClick={onSubmit}
|
onClick={onSubmit}
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
>
|
>
|
||||||
{isSubmitting ? "submitting..." : (
|
{isSubmitting ? t("inlineEdit.submitting") : (
|
||||||
<>
|
<>
|
||||||
submit
|
{t("inlineEdit.submit")}
|
||||||
<CornerDownLeft size={11} className="infio-ai-block-submit-icon" />
|
<CornerDownLeft size={11} className="infio-ai-block-submit-icon" />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@ -134,7 +136,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
|
|
||||||
const promptGenerator = new PromptGenerator(
|
const promptGenerator = new PromptGenerator(
|
||||||
async () => {
|
async () => {
|
||||||
throw new Error("RAG not needed for inline edit");
|
throw new Error(t("inlineEdit.ragNotNeeded"));
|
||||||
},
|
},
|
||||||
plugin.app,
|
plugin.app,
|
||||||
settings
|
settings
|
||||||
@ -153,19 +155,19 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
const getActiveContext = async () => {
|
const getActiveContext = async () => {
|
||||||
const activeFile = plugin.app.workspace.getActiveFile();
|
const activeFile = plugin.app.workspace.getActiveFile();
|
||||||
if (!activeFile) {
|
if (!activeFile) {
|
||||||
console.error("No active file");
|
console.error(t("inlineEdit.noActiveFile"));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const editor = plugin.app.workspace.getActiveViewOfType(MarkdownView)?.editor;
|
const editor = plugin.app.workspace.getActiveViewOfType(MarkdownView)?.editor;
|
||||||
if (!editor) {
|
if (!editor) {
|
||||||
console.error("No active editor");
|
console.error(t("inlineEdit.noActiveEditor"));
|
||||||
return { activeFile };
|
return { activeFile };
|
||||||
}
|
}
|
||||||
|
|
||||||
const selection = editor.getSelection();
|
const selection = editor.getSelection();
|
||||||
if (!selection) {
|
if (!selection) {
|
||||||
console.error("No text selected");
|
console.error(t("inlineEdit.noTextSelected"));
|
||||||
return { activeFile, editor };
|
return { activeFile, editor };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +192,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
try {
|
try {
|
||||||
const { activeFile, editor, selection } = await getActiveContext();
|
const { activeFile, editor, selection } = await getActiveContext();
|
||||||
if (!activeFile || !editor || !selection) {
|
if (!activeFile || !editor || !selection) {
|
||||||
console.error("No active file, editor, or selection");
|
console.error(t("inlineEdit.noActiveContext"));
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -201,7 +203,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
};
|
};
|
||||||
if (!chatModel) {
|
if (!chatModel) {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
throw new Error("Invalid chat model");
|
throw new Error(t("inlineEdit.invalidChatModel"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const from = editor.getCursor("from");
|
const from = editor.getCursor("from");
|
||||||
@ -238,7 +240,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
}
|
}
|
||||||
if (!response_content) {
|
if (!response_content) {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
throw new Error("Empty response from LLM");
|
throw new Error(t("inlineEdit.emptyLLMResponse"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedBlock = parseSmartComposeBlock(
|
const parsedBlock = parseSmartComposeBlock(
|
||||||
@ -248,14 +250,15 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
|
|
||||||
if (!activeFile || !(activeFile.path && typeof activeFile.path === 'string')) {
|
if (!activeFile || !(activeFile.path && typeof activeFile.path === 'string')) {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
throw new Error("Invalid active file");
|
throw new Error(t("inlineEdit.invalidActiveFile"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let fileContent: string;
|
let fileContent: string;
|
||||||
try {
|
try {
|
||||||
fileContent = await plugin.app.vault.cachedRead(activeFile);
|
fileContent = await plugin.app.vault.cachedRead(activeFile);
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Failed to read file:", error);
|
const error = err as Error;
|
||||||
|
console.error(t("inlineEdit.readFileError"), error.message);
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -268,7 +271,7 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!updatedContent) {
|
if (!updatedContent) {
|
||||||
console.error("Failed to apply changes");
|
console.error(t("inlineEdit.applyChangesError"));
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -283,8 +286,9 @@ export const InlineEdit: React.FC<InlineEditProps> = ({
|
|||||||
newContent: removeAITags(updatedContent),
|
newContent: removeAITags(updatedContent),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (err) {
|
||||||
console.error("Error in inline edit:", error);
|
const error = err as Error;
|
||||||
|
console.error(t("inlineEdit.inlineEditError"), error.message);
|
||||||
} finally {
|
} finally {
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,106 @@
|
|||||||
export default {
|
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: {
|
prompt: {
|
||||||
"title": "Prompts",
|
"title": "Prompts",
|
||||||
"description": "Click + to create a new mode",
|
"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",
|
"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",
|
"previewSystemPrompt": "Preview System Prompt",
|
||||||
"save": "Save"
|
"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 {
|
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: {
|
prompt: {
|
||||||
"title": "模型提示词设置",
|
"title": "模型提示词设置",
|
||||||
"description": "点击 + 创建新模式",
|
"description": "点击 + 创建新模式",
|
||||||
@ -28,5 +130,16 @@ export default {
|
|||||||
"overrideWarning": "。这是一个非常高级的功能,将覆盖所有内置提示,包括工具使用,请谨慎使用",
|
"overrideWarning": "。这是一个非常高级的功能,将覆盖所有内置提示,包括工具使用,请谨慎使用",
|
||||||
"previewSystemPrompt": "预览系统提示",
|
"previewSystemPrompt": "预览系统提示",
|
||||||
"save": "保存"
|
"save": "保存"
|
||||||
|
},
|
||||||
|
command: {
|
||||||
|
"createQuickCommand": "创建快捷命令",
|
||||||
|
"name": "名称",
|
||||||
|
"content": "内容",
|
||||||
|
"createCommand": "创建命令",
|
||||||
|
"searchPlaceholder": "搜索命令...",
|
||||||
|
"noCommandsFound": "未找到命令",
|
||||||
|
"updateCommand": "更新命令",
|
||||||
|
"errorContentRequired": "请输入模板内容",
|
||||||
|
"errorNameRequired": "请输入模板名称"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user