✨ feat(vscode): 问 AI 输入框旁边能选 Claude profile,记住上次选的,问答灰字写用哪个 profile 答的
This commit is contained in:
1 parent
1eb42d8389
commit
9efae1f496
20 files changed
+397
-63
No files matched your search
@@ -1,8 +1,9 @@
|
||||
import type { KeyboardEvent, ReactElement } from 'react'
|
||||
import type { Ask } from './qa'
|
||||
import type { QaEntryView } from './types'
|
||||
import type { ProfileChoice, QaEntryView } from './types'
|
||||
import { ChevronDown, ChevronRight, CircleStop, LoaderCircle, SendHorizontal, Sparkles, Trash2, TriangleAlert } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { SelectMenu } from '@/components/ui/select-menu'
|
||||
import { renderMarkdown, renderMarkdownOnce } from './markdown'
|
||||
import { answerPreview, relativeTime } from './qa'
|
||||
|
||||
@@ -17,9 +18,12 @@ function HistoryItem({ entry, now, onDelete }: { entry: QaEntryView, now: number
|
||||
<button type="button" className="cr-qa-question" aria-expanded={expanded} title={expanded ? '收起回答' : '展开整段回答'} onClick={toggle}>
|
||||
{entry.question}
|
||||
</button>
|
||||
<time className="cr-qa-when" dateTime={entry.createdAt} title={new Date(entry.createdAt).toLocaleString()}>
|
||||
{relativeTime(entry.createdAt, now)}
|
||||
</time>
|
||||
<span className="cr-qa-when">
|
||||
<time dateTime={entry.createdAt} title={new Date(entry.createdAt).toLocaleString()}>
|
||||
{relativeTime(entry.createdAt, now)}
|
||||
</time>
|
||||
{entry.profile && <span title="用这个 Claude Code 配置(profile)答的">{` · ${entry.profile}`}</span>}
|
||||
</span>
|
||||
<button type="button" className="cr-tool cr-qa-delete" title="已理解,删除" aria-label="已理解,删除" onClick={onDelete}>
|
||||
<Trash2 size={13} strokeWidth={1.8} aria-hidden />
|
||||
</button>
|
||||
@@ -69,10 +73,12 @@ function CurrentItem({ ask }: { ask: Ask }): ReactElement {
|
||||
}
|
||||
|
||||
/** 底下的输入框:Enter 发送、Shift+Enter 换行、Esc 收起;正在答时发送换成停止 */
|
||||
function AskInput({ running, first, onAsk, onStop, onClose }: {
|
||||
function AskInput({ running, first, profiles, onSelectProfile, onAsk, onStop, onClose }: {
|
||||
running: Ask | undefined
|
||||
/** 这块还没问过:提示「问这块代码…」,问过了是「继续问…」 */
|
||||
first: boolean
|
||||
profiles: ProfileChoice
|
||||
onSelectProfile: (id: string) => void
|
||||
onAsk: (question: string) => void
|
||||
onStop: (askId: string) => void
|
||||
onClose: () => void
|
||||
@@ -107,6 +113,15 @@ function AskInput({ running, first, onAsk, onStop, onClose }: {
|
||||
onChange={event => setValue(event.target.value)}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
<SelectMenu
|
||||
variant="ghost"
|
||||
value={profiles.selected}
|
||||
options={profiles.options.map(option => ({ value: option.id, label: option.label }))}
|
||||
onChange={onSelectProfile}
|
||||
placeholder="默认"
|
||||
title="用哪个 Claude Code 配置(profile)回答,下一个问题起用它"
|
||||
className="cr-qa-profile"
|
||||
/>
|
||||
{running
|
||||
? (
|
||||
<button type="button" className="cr-tool" title="停止" aria-label="停止" onClick={() => onStop(running.askId)}>
|
||||
@@ -127,7 +142,10 @@ export interface AskThreadProps {
|
||||
asks: Ask[]
|
||||
/** 点了「问 AI」:展开全部历史、当前这一轮和输入框 */
|
||||
open: boolean
|
||||
/** 输入框旁边的 profile 下拉 */
|
||||
profiles: ProfileChoice
|
||||
now?: number
|
||||
onSelectProfile: (id: string) => void
|
||||
onAsk: (question: string) => void
|
||||
onStop: (askId: string) => void
|
||||
onDelete: (entryId: string) => void
|
||||
@@ -138,7 +156,7 @@ export interface AskThreadProps {
|
||||
* 块下面的问答区(左边一条竖线):展开时是灰字的历史、当前这一轮、输入框;
|
||||
* 没展开但问过的块只露一行「问过 N 次」,点开看灰字历史,读代码时一眼知道这里问过什么。
|
||||
*/
|
||||
export function AskThread({ history, asks, open, now = Date.now(), onAsk, onStop, onDelete, onClose }: AskThreadProps): ReactElement | null {
|
||||
export function AskThread({ history, asks, open, profiles, now = Date.now(), onSelectProfile, onAsk, onStop, onDelete, onClose }: AskThreadProps): ReactElement | null {
|
||||
const [showHistory, setShowHistory] = useState(false)
|
||||
if (!open) {
|
||||
if (history.length === 0)
|
||||
@@ -163,6 +181,8 @@ export function AskThread({ history, asks, open, now = Date.now(), onAsk, onStop
|
||||
<AskInput
|
||||
running={asks.find(ask => ask.status === 'running')}
|
||||
first={history.length === 0 && asks.length === 0}
|
||||
profiles={profiles}
|
||||
onSelectProfile={onSelectProfile}
|
||||
onAsk={onAsk}
|
||||
onStop={onStop}
|
||||
onClose={onClose}
|
||||
|
||||
Reference in new issue
Block a user