add mode custom

This commit is contained in:
duanfuxiang
2025-04-28 23:03:53 +08:00
parent 497a9739d7
commit f282c9f667
7 changed files with 103 additions and 23 deletions

View File

@@ -30,6 +30,7 @@ import {
} from '../../core/llm/exception'
import { regexSearchFiles } from '../../core/ripgrep'
import { useChatHistory } from '../../hooks/use-chat-history'
import { useCustomModes } from '../../hooks/use-custom-mode'
import { ApplyStatus, ToolArgs } from '../../types/apply'
import { ChatMessage, ChatUserMessage } from '../../types/chat'
import {
@@ -101,6 +102,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
const { settings, setSettings } = useSettings()
const { getRAGEngine } = useRAG()
const diffStrategy = useDiffStrategy()
const { customModeList, customModePrompts } = useCustomModes()
const {
createOrUpdateConversation,
@@ -112,8 +114,8 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
const { streamResponse, chatModel } = useLLM()
const promptGenerator = useMemo(() => {
return new PromptGenerator(getRAGEngine, app, settings, diffStrategy)
}, [getRAGEngine, app, settings, diffStrategy])
return new PromptGenerator(getRAGEngine, app, settings, diffStrategy, customModePrompts, customModeList)
}, [getRAGEngine, app, settings, diffStrategy, customModePrompts, customModeList])
const [inputMessage, setInputMessage] = useState<ChatUserMessage>(() => {
const newMessage = getNewInputMessage(app, settings.defaultMention)

View File

@@ -1,10 +1,14 @@
import { Plus, Undo2, Settings, Circle, Trash2 } from 'lucide-react';
import React, { useState, useEffect } from 'react';
import { ChevronDown, ChevronRight, Plus, Trash2, Undo2 } from 'lucide-react';
import React, { useEffect, useState } from 'react';
import { useApp } from '../../contexts/AppContext';
import { CustomMode, GroupEntry, ToolGroup } from '../../database/json/custom-mode/types';
import { useCustomModes } from '../../hooks/use-custom-mode';
import { CustomMode, ToolGroup, toolGroups, GroupEntry } from '../../database/json/custom-mode/types';
import { modes as buildinModes } from '../../utils/modes';
import { openOrCreateMarkdownFile } from '../../utils/obsidian';
const CustomModeView = () => {
const app = useApp()
const {
createCustomMode,
deleteCustomMode,
@@ -15,7 +19,7 @@ const CustomModeView = () => {
// 当前选择的模式
const [selectedMode, setSelectedMode] = useState<string>('ask')
const [isBuiltinMode, setIsBuiltinMode] = useState<boolean>(true)
const [isAdvancedCollapsed, setIsAdvancedCollapsed] = useState(true);
const isNewMode = React.useMemo(() => selectedMode === "add_new_mode", [selectedMode])
@@ -46,7 +50,6 @@ const CustomModeView = () => {
// 自定义指令
const [customInstructions, setCustomInstructions] = useState<string>('')
// 当模式变更时更新表单数据
useEffect(() => {
// new mode
@@ -63,7 +66,7 @@ const CustomModeView = () => {
const builtinMode = buildinModes.find(m => m.slug === selectedMode);
if (builtinMode) {
setIsBuiltinMode(true);
setModeName(builtinMode.name);
setModeName(builtinMode.slug);
setRoleDefinition(builtinMode.roleDefinition);
setCustomInstructions(builtinMode.customInstructions || '');
setSelectedTools(builtinMode.groups as GroupEntry[]);
@@ -219,9 +222,11 @@ const CustomModeView = () => {
<div className="infio-custom-modes-section">
<div className="infio-section-header">
<h3></h3>
<button className="infio-section-btn">
<Undo2 size={16} />
</button>
{isBuiltinMode && (
<button className="infio-section-btn">
<Undo2 size={16} />
</button>
)}
</div>
<p className="infio-section-subtitle"></p>
<textarea
@@ -274,7 +279,7 @@ const CustomModeView = () => {
checked={selectedTools.includes('research')}
onChange={() => handleToolChange('research')}
/>
</label>
</div>
</div>
@@ -298,10 +303,29 @@ const CustomModeView = () => {
placeholder="输入模式自定义指令..."
/>
<p className="infio-section-footer">
<a href="#" className="infio-link">_infio_prompts/code-rules/</a>
<a href="#" className="infio-link" onClick={() => openOrCreateMarkdownFile(app, `_infio_prompts/${modeName}/rules.md`, 0)}>_infio_prompts/{modeName}/rules</a>
</p>
</div>
{/* 高级, 覆盖系统提示词 */}
<div className="infio-custom-modes-section">
<div
className="infio-section-header infio-section-header-collapsible"
onClick={() => setIsAdvancedCollapsed(!isAdvancedCollapsed)}
>
<div className="infio-section-header-title-container">
{isAdvancedCollapsed ? <ChevronRight size={16} /> : <ChevronDown size={16} />}
<h6></h6>
</div>
</div>
{!isAdvancedCollapsed && (
<p className="infio-section-subtitle">
<a href="#" className="infio-link" onClick={() => openOrCreateMarkdownFile(app, `_infio_prompts/${modeName}/system-prompt.md`, 0)}>_infio_prompts/{modeName}/system-prompt</a>
使,
</p>
)}
</div>
<div className="infio-custom-modes-actions">
<button className="infio-preview-btn">
@@ -546,6 +570,17 @@ const CustomModeView = () => {
justify-content: center;
width: fit-content;
}
.infio-section-header-collapsible {
cursor: pointer;
user-select: none;
}
.infio-section-header-title-container {
display: flex;
align-items: center;
gap: 4px;
}
`}
</style>
</div>

View File

@@ -1,8 +1,9 @@
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
import { ChevronDown, ChevronUp } from 'lucide-react'
import { useEffect, useState } from 'react'
import React, { useEffect, useState, useMemo } from 'react'
import { useSettings } from '../../../contexts/SettingsContext'
import { useCustomModes } from '../../../hooks/use-custom-mode'
import { modes } from '../../../utils/modes'
export function ModeSelect() {
@@ -10,11 +11,14 @@ export function ModeSelect() {
const [isOpen, setIsOpen] = useState(false)
const [mode, setMode] = useState(settings.mode)
const { customModeList } = useCustomModes()
const allModes = useMemo(() => [...modes, ...customModeList], [customModeList])
useEffect(() => {
setMode(settings.mode)
}, [settings.mode])
return (
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
<DropdownMenu.Trigger className="infio-chat-input-model-select">
@@ -22,7 +26,7 @@ export function ModeSelect() {
{isOpen ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
</div>
<div className="infio-chat-input-model-select__model-name">
{modes.find((m) => m.slug === mode)?.name}
{allModes.find((m) => m.slug === mode)?.name}
</div>
</DropdownMenu.Trigger>
@@ -30,7 +34,7 @@ export function ModeSelect() {
<DropdownMenu.Content
className="infio-popover">
<ul>
{modes.map((mode) => (
{allModes.map((mode) => (
<DropdownMenu.Item
key={mode.slug}
onSelect={() => {