mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-01-16 16:31:56 +00:00
update chatview
This commit is contained in:
parent
b3e16d6bcb
commit
87c79b45d6
@ -56,7 +56,6 @@ import { PromptGenerator, addLineNumbers } from '../../utils/prompt-generator'
|
|||||||
// Removed empty line above, added one below for group separation
|
// Removed empty line above, added one below for group separation
|
||||||
import { fetchUrlsContent, onEnt, webSearch } from '../../utils/web-search'
|
import { fetchUrlsContent, onEnt, webSearch } from '../../utils/web-search'
|
||||||
|
|
||||||
import { ModeSelect } from './chat-input/ModeSelect'; // Start of new group
|
|
||||||
import PromptInputWithActions, { ChatUserInputRef } from './chat-input/PromptInputWithActions'
|
import PromptInputWithActions, { ChatUserInputRef } from './chat-input/PromptInputWithActions'
|
||||||
import { editorStateToPlainText } from './chat-input/utils/editor-state-to-plain-text'
|
import { editorStateToPlainText } from './chat-input/utils/editor-state-to-plain-text'
|
||||||
import ChatHistoryView from './ChatHistoryView'
|
import ChatHistoryView from './ChatHistoryView'
|
||||||
@ -986,7 +985,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
|
|||||||
<div className="infio-chat-container">
|
<div className="infio-chat-container">
|
||||||
{/* header view */}
|
{/* header view */}
|
||||||
<div className="infio-chat-header">
|
<div className="infio-chat-header">
|
||||||
<ModeSelect />
|
INFIO
|
||||||
<div className="infio-chat-header-buttons">
|
<div className="infio-chat-header-buttons">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@ -120,7 +120,6 @@ const UserMessageView: React.FC<UserMessageViewProps> = ({
|
|||||||
border: 2px solid var(--background-modifier-border);
|
border: 2px solid var(--background-modifier-border);
|
||||||
border-radius: var(--radius-s);
|
border-radius: var(--radius-s);
|
||||||
padding: calc(var(--size-2-2) + 1px);
|
padding: calc(var(--size-2-2) + 1px);
|
||||||
min-height: 62px;
|
|
||||||
gap: var(--size-2-2);
|
gap: var(--size-2-2);
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||||
transition: all 0.15s ease-in-out;
|
transition: all 0.15s ease-in-out;
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
import { ImageIcon } from 'lucide-react'
|
import { ImageUp } 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({
|
||||||
@ -33,9 +32,9 @@ export function ImageUploadButton({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<div className="infio-chat-user-input-submit-button-icons">
|
<div className="infio-chat-user-input-submit-button-icons">
|
||||||
<ImageIcon size={12} />
|
<ImageUp size={14} />
|
||||||
</div>
|
</div>
|
||||||
<div>{t('chat.input.image')}</div>
|
{/* <div>{t('chat.input.image')}</div> */}
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react'
|
import { ChevronDown, ChevronUp, MessageSquare, SquarePen, Search } from 'lucide-react'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { useSettings } from '../../../contexts/SettingsContext'
|
import { useSettings } from '../../../contexts/SettingsContext'
|
||||||
@ -21,39 +21,178 @@ export function ModeSelect() {
|
|||||||
setMode(settings.mode)
|
setMode(settings.mode)
|
||||||
}, [settings.mode])
|
}, [settings.mode])
|
||||||
|
|
||||||
return (
|
// 为默认模式定义快捷键提示
|
||||||
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
|
const getShortcutText = (slug: string) => {
|
||||||
<DropdownMenu.Trigger className="infio-chat-input-model-select">
|
switch (slug) {
|
||||||
<div className="infio-chat-input-model-select__icon">
|
case 'write':
|
||||||
{isOpen ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
|
return 'Cmd+Shift+.'
|
||||||
</div>
|
case 'ask':
|
||||||
<div className="infio-chat-input-model-select__model-name">
|
return 'Cmd+Shift+,'
|
||||||
{allModes.find((m) => m.slug === mode)?.name}
|
case 'research':
|
||||||
</div>
|
return 'Cmd+Shift+/'
|
||||||
</DropdownMenu.Trigger>
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<DropdownMenu.Portal>
|
// 为默认模式定义图标
|
||||||
<DropdownMenu.Content
|
const getModeIcon = (slug: string) => {
|
||||||
className="infio-popover">
|
switch (slug) {
|
||||||
<ul>
|
case 'ask':
|
||||||
{allModes.map((mode) => (
|
return <MessageSquare size={14} />
|
||||||
<DropdownMenu.Item
|
case 'write':
|
||||||
key={mode.slug}
|
return <SquarePen size={14} />
|
||||||
onSelect={() => {
|
case 'research':
|
||||||
setMode(mode.slug)
|
return <Search size={14} />
|
||||||
setSettings({
|
default:
|
||||||
...settings,
|
return null
|
||||||
mode: mode.slug,
|
}
|
||||||
})
|
}
|
||||||
}}
|
|
||||||
asChild
|
|
||||||
>
|
return (
|
||||||
<li>{mode.name}</li>
|
<>
|
||||||
</DropdownMenu.Item>
|
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
))}
|
<DropdownMenu.Trigger className="infio-chat-input-mode-select">
|
||||||
</ul>
|
<span className="infio-mode-icon">{getModeIcon(mode)}</span>
|
||||||
</DropdownMenu.Content>
|
<div className="infio-chat-input-mode-select__model-name">
|
||||||
</DropdownMenu.Portal>
|
{allModes.find((m) => m.slug === mode)?.name}
|
||||||
</DropdownMenu.Root>
|
</div>
|
||||||
|
<div className="infio-chat-input-mode-select__icon">
|
||||||
|
{isOpen ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
|
||||||
|
</div>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
|
<DropdownMenu.Portal>
|
||||||
|
<DropdownMenu.Content
|
||||||
|
className="infio-popover infio-mode-select-content">
|
||||||
|
<ul>
|
||||||
|
{allModes.map((mode) => {
|
||||||
|
const shortcut = getShortcutText(mode.slug)
|
||||||
|
const icon = getModeIcon(mode.slug)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu.Item
|
||||||
|
key={mode.slug}
|
||||||
|
onSelect={() => {
|
||||||
|
setMode(mode.slug)
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
mode: mode.slug,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<li className="infio-mode-item">
|
||||||
|
<div className="infio-mode-left">
|
||||||
|
{icon && (
|
||||||
|
<span className="infio-mode-icon">{icon}</span>
|
||||||
|
)}
|
||||||
|
<span className="infio-mode-name">{mode.name}</span>
|
||||||
|
</div>
|
||||||
|
{shortcut && (
|
||||||
|
<span className="infio-mode-shortcut">{shortcut}</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Portal>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
<style >{`
|
||||||
|
button.infio-chat-input-mode-select {
|
||||||
|
background-color: var(--background-modifier-hover);
|
||||||
|
box-shadow: none;
|
||||||
|
border: 1;
|
||||||
|
padding: var(--size-4-1) var(--size-4-2);
|
||||||
|
font-size: var(--font-smallest);
|
||||||
|
font-weight: var(--font-medium);
|
||||||
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
height: var(--size-4-4);
|
||||||
|
max-width: 100%;
|
||||||
|
gap: var(--size-2-2);
|
||||||
|
border-radius: var(--radius-m);
|
||||||
|
transition: all 0.15s ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--text-normal);
|
||||||
|
background-color: var(--background-modifier-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-chat-input-mode-select__mode-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--text-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-chat-input-mode-select__model-name {
|
||||||
|
flex-shrink: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-chat-input-mode-select__icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-mode-select-content {
|
||||||
|
min-width: auto !important;
|
||||||
|
width: fit-content !important;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-mode-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--size-4-2) var(--size-4-2);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-mode-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--size-2-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-mode-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--text-accent);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-mode-name {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-mode-shortcut {
|
||||||
|
font-size: var(--font-smallest);
|
||||||
|
color: var(--text-muted);
|
||||||
|
background-color: var(--background-modifier-border);
|
||||||
|
padding: var(--size-2-1) var(--size-2-2);
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
font-family: var(--font-monospace);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
||||||
import Fuse, { FuseResult } from 'fuse.js'
|
import Fuse, { FuseResult } from 'fuse.js'
|
||||||
import { ChevronDown, ChevronUp, Star, StarOff } from 'lucide-react'
|
import { Brain, ChevronDown, ChevronUp, Star } 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'
|
||||||
@ -8,6 +8,34 @@ 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"
|
||||||
|
|
||||||
|
// 优化模型名称显示的函数
|
||||||
|
const getOptimizedModelName = (modelId: string): string => {
|
||||||
|
if (!modelId) return modelId;
|
||||||
|
|
||||||
|
// 移除常见的前缀
|
||||||
|
let optimized = modelId
|
||||||
|
.replace(/^(anthropic\/|openai\/|google\/|meta\/|microsoft\/|huggingface\/|mistral\/|cohere\/|ai21\/|together\/|perplexity\/|groq\/|deepseek\/|qwen\/|alibaba\/|baichuan\/|chatglm\/|yi\/|moonshot\/|zhipu\/|minimax\/|sensetime\/|iflytek\/|tencent\/|baidu\/|bytedance\/|netease\/|360\/|xunfei\/|spark\/|ernie\/|wenxin\/|tongyi\/|claude\/|gpt-|llama-|gemini-|palm-|bard-|codex-|davinci-|curie-|babbage-|ada-)/i, '')
|
||||||
|
// 移除版本号和日期
|
||||||
|
.replace(/(-v?\d+(\.\d+)*(-\w+)?|:\d+(\.\d+)*|@\d+(\.\d+)*|-\d{4}-\d{2}-\d{2}|-\d{8}|-latest|-preview|-beta|-alpha|-rc\d*|-instruct|-chat|-base|-turbo|-16k|-32k|-128k)$/i, '')
|
||||||
|
// 移除多余的连字符和下划线
|
||||||
|
.replace(/[-_]+/g, '-')
|
||||||
|
.replace(/^-+|-+$/g, '');
|
||||||
|
|
||||||
|
// 如果优化后的名称太短或为空,返回原始名称的简化版本
|
||||||
|
if (optimized.length < 3) {
|
||||||
|
// 尝试提取主要部分
|
||||||
|
const parts = modelId.split(/[\/\-_:@]/);
|
||||||
|
optimized = parts.find(part => part.length >= 3) || modelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 限制长度,如果太长则截断并添加省略号
|
||||||
|
if (optimized.length > 25) {
|
||||||
|
optimized = optimized.substring(0, 22) + '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
return optimized;
|
||||||
|
};
|
||||||
|
|
||||||
type TextSegment = {
|
type TextSegment = {
|
||||||
text: string;
|
text: string;
|
||||||
isHighlighted: boolean;
|
isHighlighted: boolean;
|
||||||
@ -187,12 +215,12 @@ export function ModelSelect() {
|
|||||||
const fuse = useMemo(() => {
|
const fuse = useMemo(() => {
|
||||||
return new Fuse<SearchableItem>(searchableItems, {
|
return new Fuse<SearchableItem>(searchableItems, {
|
||||||
keys: ["html"],
|
keys: ["html"],
|
||||||
threshold: 0.6,
|
threshold: 1,
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
isCaseSensitive: false,
|
isCaseSensitive: false,
|
||||||
ignoreLocation: false,
|
ignoreLocation: false,
|
||||||
includeMatches: true,
|
includeMatches: true,
|
||||||
minMatchCharLength: 1,
|
minMatchCharLength: 2,
|
||||||
})
|
})
|
||||||
}, [searchableItems])
|
}, [searchableItems])
|
||||||
|
|
||||||
@ -243,12 +271,18 @@ export function ModelSelect() {
|
|||||||
<>
|
<>
|
||||||
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
|
<DropdownMenu.Root open={isOpen} onOpenChange={setIsOpen}>
|
||||||
<DropdownMenu.Trigger className="infio-chat-input-model-select">
|
<DropdownMenu.Trigger className="infio-chat-input-model-select">
|
||||||
|
{/* <div className="infio-chat-input-model-select__mode-icon">
|
||||||
|
<Brain size={16} />
|
||||||
|
</div> */}
|
||||||
|
<div
|
||||||
|
className="infio-chat-input-model-select__model-name"
|
||||||
|
title={chatModelId}
|
||||||
|
>
|
||||||
|
{getOptimizedModelName(chatModelId)}
|
||||||
|
</div>
|
||||||
<div className="infio-chat-input-model-select__icon">
|
<div className="infio-chat-input-model-select__icon">
|
||||||
{isOpen ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
|
{isOpen ? <ChevronUp size={12} /> : <ChevronDown size={12} />}
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-chat-input-model-select__model-name">
|
|
||||||
{chatModelId}
|
|
||||||
</div>
|
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
<DropdownMenu.Portal>
|
<DropdownMenu.Portal>
|
||||||
@ -283,7 +317,7 @@ export function ModelSelect() {
|
|||||||
>
|
>
|
||||||
<div className="infio-model-item-text-wrapper">
|
<div className="infio-model-item-text-wrapper">
|
||||||
<span className="infio-provider-badge">{collectedModel.provider}</span>
|
<span className="infio-provider-badge">{collectedModel.provider}</span>
|
||||||
<span>{collectedModel.modelId}</span>
|
<span title={collectedModel.modelId}>{getOptimizedModelName(collectedModel.modelId)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="infio-model-item-star"
|
className="infio-model-item-star"
|
||||||
@ -454,7 +488,11 @@ export function ModelSelect() {
|
|||||||
title={option.id}
|
title={option.id}
|
||||||
>
|
>
|
||||||
<div className="infio-model-item-text-wrapper">
|
<div className="infio-model-item-text-wrapper">
|
||||||
<HighlightedText segments={option.html} />
|
{searchTerm ? (
|
||||||
|
<HighlightedText segments={option.html} />
|
||||||
|
) : (
|
||||||
|
<span title={option.id}>{getOptimizedModelName(option.id)}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="infio-model-item-star"
|
className="infio-model-item-star"
|
||||||
@ -503,6 +541,15 @@ export function ModelSelect() {
|
|||||||
display: block;
|
display: block;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Model name display optimization */
|
||||||
|
.infio-chat-input-model-select__model-name {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 200px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
.infio-llm-setting-model-item {
|
.infio-llm-setting-model-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
|
|
||||||
import { useApp } from '../../../contexts/AppContext'
|
import { useApp } from '../../../contexts/AppContext'
|
||||||
import { useDarkModeContext } from '../../../contexts/DarkModeContext'
|
import { useDarkModeContext } from '../../../contexts/DarkModeContext'
|
||||||
|
import { useSettings } from '../../../contexts/SettingsContext'
|
||||||
import {
|
import {
|
||||||
Mentionable,
|
Mentionable,
|
||||||
MentionableImage,
|
MentionableImage,
|
||||||
@ -31,11 +32,10 @@ import { ImageUploadButton } from './ImageUploadButton'
|
|||||||
import LexicalContentEditable from './LexicalContentEditable'
|
import LexicalContentEditable from './LexicalContentEditable'
|
||||||
import MentionableBadge from './MentionableBadge'
|
import MentionableBadge from './MentionableBadge'
|
||||||
import { ModelSelect } from './ModelSelect'
|
import { ModelSelect } from './ModelSelect'
|
||||||
// import { ModeSelect } from './ModeSelect'
|
import { ModeSelect } from './ModeSelect'
|
||||||
import { MentionNode } from './plugins/mention/MentionNode'
|
import { MentionNode } from './plugins/mention/MentionNode'
|
||||||
import { NodeMutations } from './plugins/on-mutation/OnMutationPlugin'
|
import { NodeMutations } from './plugins/on-mutation/OnMutationPlugin'
|
||||||
import { SubmitButton } from './SubmitButton'
|
import { SubmitButton } from './SubmitButton'
|
||||||
|
|
||||||
export type ChatUserInputRef = {
|
export type ChatUserInputRef = {
|
||||||
focus: () => void
|
focus: () => void
|
||||||
}
|
}
|
||||||
@ -68,6 +68,7 @@ const PromptInputWithActions = forwardRef<ChatUserInputRef, ChatUserInputProps>(
|
|||||||
ref,
|
ref,
|
||||||
) => {
|
) => {
|
||||||
const app = useApp()
|
const app = useApp()
|
||||||
|
const { settings, setSettings } = useSettings()
|
||||||
|
|
||||||
const editorRef = useRef<LexicalEditor | null>(null)
|
const editorRef = useRef<LexicalEditor | null>(null)
|
||||||
const contentEditableRef = useRef<HTMLDivElement>(null)
|
const contentEditableRef = useRef<HTMLDivElement>(null)
|
||||||
@ -83,6 +84,50 @@ const PromptInputWithActions = forwardRef<ChatUserInputRef, ChatUserInputProps>(
|
|||||||
}
|
}
|
||||||
}, [addedBlockKey])
|
}, [addedBlockKey])
|
||||||
|
|
||||||
|
// 添加快捷键监听器
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
// 检查是否按下了 Cmd + Shift 键 (macOS)
|
||||||
|
if (event.ctrlKey && event.shiftKey) {
|
||||||
|
// 使用 event.key 直接匹配,不使用 toLowerCase()
|
||||||
|
switch (event.key) {
|
||||||
|
case '.':
|
||||||
|
case '>': // Shift + . 在某些键盘布局下可能是 >
|
||||||
|
event.preventDefault()
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
mode: 'write',
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case ',':
|
||||||
|
case '<': // Shift + , 在某些键盘布局下可能是 <
|
||||||
|
event.preventDefault()
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
mode: 'ask',
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case '/':
|
||||||
|
case '?': // Shift + / 在某些键盘布局下可能是 ?
|
||||||
|
event.preventDefault()
|
||||||
|
setSettings({
|
||||||
|
...settings,
|
||||||
|
mode: 'research',
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加事件监听器到 document
|
||||||
|
document.addEventListener('keydown', handleKeyDown)
|
||||||
|
|
||||||
|
// 清理函数
|
||||||
|
return () => {
|
||||||
|
document.removeEventListener('keydown', handleKeyDown)
|
||||||
|
}
|
||||||
|
}, [settings, setSettings])
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
focus: () => {
|
focus: () => {
|
||||||
contentEditableRef.current?.focus()
|
contentEditableRef.current?.focus()
|
||||||
@ -278,10 +323,11 @@ const PromptInputWithActions = forwardRef<ChatUserInputRef, ChatUserInputProps>(
|
|||||||
|
|
||||||
<div className="infio-chat-user-input-controls">
|
<div className="infio-chat-user-input-controls">
|
||||||
<div className="infio-chat-user-input-controls__model-select-container">
|
<div className="infio-chat-user-input-controls__model-select-container">
|
||||||
|
<ModeSelect />
|
||||||
<ModelSelect />
|
<ModelSelect />
|
||||||
<ImageUploadButton onUpload={handleUploadImages} />
|
|
||||||
</div>
|
</div>
|
||||||
<div className="infio-chat-user-input-controls__buttons">
|
<div className="infio-chat-user-input-controls__buttons">
|
||||||
|
<ImageUploadButton onUpload={handleUploadImages} />
|
||||||
<SubmitButton onClick={() => handleSubmit()} />
|
<SubmitButton onClick={() => handleSubmit()} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,14 +1,53 @@
|
|||||||
import { CornerDownLeftIcon } from 'lucide-react'
|
import { ArrowUpIcon } from 'lucide-react'
|
||||||
|
|
||||||
import { t } from '../../../lang/helpers'
|
// 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}>
|
<>
|
||||||
{t('chat.input.submit')}
|
<button className="infio-chat-user-input-submit1-button" onClick={onClick}>
|
||||||
<div className="infio-chat-user-input-submit-button-icons">
|
{/* {t('chat.input.submit')} */}
|
||||||
<CornerDownLeftIcon size={12} />
|
<div className="infio-chat-user-input-submit1-button-icons">
|
||||||
</div>
|
<ArrowUpIcon size={14} />
|
||||||
</button>
|
</div>
|
||||||
)
|
</button>
|
||||||
|
<style>
|
||||||
|
{`
|
||||||
|
.infio-chat-user-input-controls .infio-chat-user-input-submit1-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--size-4-1);
|
||||||
|
font-size: var(--font-smallest);
|
||||||
|
color: var(--text-on-accent);
|
||||||
|
background-color: var(--interactive-accent);
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: var(--size-4-5);
|
||||||
|
height: var(--size-4-5);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--interactive-accent-hover);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-chat-user-input-submit-button-icons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
</style>
|
||||||
|
</>
|
||||||
|
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
22
styles.css
22
styles.css
@ -331,7 +331,7 @@ button:not(.clickable-icon).infio-chat-list-dropdown {
|
|||||||
.infio-chat-user-input-controls__buttons {
|
.infio-chat-user-input-controls__buttons {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--size-4-2);
|
gap: var(--size-4-3);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1894,19 +1894,32 @@ select.infio-ai-block-model-select::-ms-expand {
|
|||||||
button.infio-chat-input-model-select {
|
button.infio-chat-input-model-select {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border: 0;
|
border: 1;
|
||||||
padding: 0;
|
padding: var(--size-2-1) var(--size-2-2);
|
||||||
font-size: var(--font-smallest);
|
font-size: var(--font-smallest);
|
||||||
font-weight: var(--font-medium);
|
font-weight: var(--font-medium);
|
||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
|
display: flex;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: var(--size-4-4);
|
height: var(--size-4-4);
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
gap: var(--size-2-2);
|
||||||
|
border-radius: var(--radius-s);
|
||||||
|
transition: all 0.15s ease-in-out;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--text-normal);
|
color: var(--text-normal);
|
||||||
|
background-color: var(--background-modifier-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.infio-chat-input-model-select__mode-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--text-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.infio-chat-input-model-select__model-name {
|
.infio-chat-input-model-select__model-name {
|
||||||
@ -1914,6 +1927,7 @@ button.infio-chat-input-model-select {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.infio-chat-input-model-select__icon {
|
.infio-chat-input-model-select__icon {
|
||||||
@ -1921,6 +1935,7 @@ button.infio-chat-input-model-select {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2418,4 +2433,3 @@ button.infio-chat-input-model-select {
|
|||||||
max-width: 80vw;
|
max-width: 80vw;
|
||||||
max-height: 80vh;
|
max-height: 80vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user